aboutsummaryrefslogtreecommitdiffhomepage
path: root/audio
diff options
context:
space:
mode:
authorGravatar wm4 <wm4@nowhere>2012-11-05 17:02:04 +0100
committerGravatar wm4 <wm4@nowhere>2012-11-12 20:06:14 +0100
commitd4bdd0473d6f43132257c9fb3848d829755167a3 (patch)
tree8021c2f7da1841393c8c832105e20cd527826d6c /audio
parentbd48deba77bd5582c5829d6fe73a7d2571088aba (diff)
Rename directories, move files (step 1 of 2) (does not compile)
Tis drops the silly lib prefixes, and attempts to organize the tree in a more logical way. Make the top-level directory less cluttered as well. Renames the following directories: libaf -> audio/filter libao2 -> audio/out libvo -> video/out libmpdemux -> demux Split libmpcodecs: vf* -> video/filter vd*, dec_video.* -> video/decode mp_image*, img_format*, ... -> video/ ad*, dec_audio.* -> audio/decode libaf/format.* is moved to audio/ - this is similar to how mp_image.* is located in video/. Move most top-level .c/.h files to core. (talloc.c/.h is left on top- level, because it's external.) Park some of the more annoying files in compat/. Some of these are relicts from the time mplayer used ffmpeg internals. sub/ is not split, because it's too much of a mess (subtitle code is mixed with OSD display and rendering). Maybe the organization of core is not ideal: it mixes playback core (like mplayer.c) and utility helpers (like bstr.c/h). Should the need arise, the playback core will be moved somewhere else, while core contains all helper and common code.
Diffstat (limited to 'audio')
-rw-r--r--audio/decode/ad.c50
-rw-r--r--audio/decode/ad.h54
-rw-r--r--audio/decode/ad_dvdpcm.c162
-rw-r--r--audio/decode/ad_internal.h46
-rw-r--r--audio/decode/ad_lavc.c413
-rw-r--r--audio/decode/ad_mpg123.c489
-rw-r--r--audio/decode/ad_pcm.c220
-rw-r--r--audio/decode/ad_spdif.c310
-rw-r--r--audio/decode/dec_audio.c462
-rw-r--r--audio/decode/dec_audio.h38
-rw-r--r--audio/filter/af.c700
-rw-r--r--audio/filter/af.h349
-rw-r--r--audio/filter/af_bs2b.c274
-rw-r--r--audio/filter/af_center.c129
-rw-r--r--audio/filter/af_channels.c306
-rw-r--r--audio/filter/af_delay.c200
-rw-r--r--audio/filter/af_dummy.c76
-rw-r--r--audio/filter/af_equalizer.c248
-rw-r--r--audio/filter/af_export.c273
-rw-r--r--audio/filter/af_extrastereo.c157
-rw-r--r--audio/filter/af_format.c519
-rw-r--r--audio/filter/af_format_alaw.h324
-rw-r--r--audio/filter/af_format_ulaw.h837
-rw-r--r--audio/filter/af_hrtf.c670
-rw-r--r--audio/filter/af_hrtf.h511
-rw-r--r--audio/filter/af_karaoke.c98
-rw-r--r--audio/filter/af_ladspa.c915
-rw-r--r--audio/filter/af_lavcac3enc.c332
-rw-r--r--audio/filter/af_lavcresample.c213
-rw-r--r--audio/filter/af_pan.c210
-rw-r--r--audio/filter/af_resample.c394
-rw-r--r--audio/filter/af_resample_template.c171
-rw-r--r--audio/filter/af_scaletempo.c581
-rw-r--r--audio/filter/af_sinesuppress.c184
-rw-r--r--audio/filter/af_sub.c188
-rw-r--r--audio/filter/af_surround.c273
-rw-r--r--audio/filter/af_sweep.c103
-rw-r--r--audio/filter/af_tools.c110
-rw-r--r--audio/filter/af_volnorm.c353
-rw-r--r--audio/filter/af_volume.c226
-rw-r--r--audio/filter/control.h257
-rw-r--r--audio/filter/dsp.h32
-rw-r--r--audio/filter/equalizer.h48
-rw-r--r--audio/filter/filter.c360
-rw-r--r--audio/filter/filter.h75
-rw-r--r--audio/filter/window.c213
-rw-r--r--audio/filter/window.h43
-rw-r--r--audio/format.c134
-rw-r--r--audio/format.h137
-rw-r--r--audio/mixer.c292
-rw-r--r--audio/mixer.h61
-rw-r--r--audio/out/ao.c294
-rw-r--r--audio/out/ao.h140
-rw-r--r--audio/out/ao_alsa.c868
-rw-r--r--audio/out/ao_coreaudio.c1283
-rw-r--r--audio/out/ao_dsound.c648
-rw-r--r--audio/out/ao_jack.c361
-rw-r--r--audio/out/ao_lavc.c621
-rw-r--r--audio/out/ao_null.c129
-rw-r--r--audio/out/ao_openal.c280
-rw-r--r--audio/out/ao_oss.c560
-rw-r--r--audio/out/ao_pcm.c256
-rw-r--r--audio/out/ao_portaudio.c431
-rw-r--r--audio/out/ao_pulse.c554
-rw-r--r--audio/out/ao_rsound.c214
-rw-r--r--audio/out/audio_out_internal.h65
-rw-r--r--audio/reorder_ch.c1400
-rw-r--r--audio/reorder_ch.h133
68 files changed, 22057 insertions, 0 deletions
diff --git a/audio/decode/ad.c b/audio/decode/ad.c
new file mode 100644
index 0000000000..93cebed86d
--- /dev/null
+++ b/audio/decode/ad.c
@@ -0,0 +1,50 @@
+/*
+ * audio decoder interface
+ *
+ * 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 <string.h>
+
+#include "config.h"
+
+#include "stream/stream.h"
+#include "libmpdemux/demuxer.h"
+#include "libmpdemux/stheader.h"
+#include "ad.h"
+
+/* Missed vorbis, mad, dshow */
+
+extern const ad_functions_t mpcodecs_ad_mpg123;
+extern const ad_functions_t mpcodecs_ad_ffmpeg;
+extern const ad_functions_t mpcodecs_ad_pcm;
+extern const ad_functions_t mpcodecs_ad_dvdpcm;
+extern const ad_functions_t mpcodecs_ad_spdif;
+
+const ad_functions_t * const mpcodecs_ad_drivers[] =
+{
+#ifdef CONFIG_MPG123
+ &mpcodecs_ad_mpg123,
+#endif
+ &mpcodecs_ad_ffmpeg,
+ &mpcodecs_ad_pcm,
+ &mpcodecs_ad_dvdpcm,
+ &mpcodecs_ad_spdif,
+ NULL
+};
diff --git a/audio/decode/ad.h b/audio/decode/ad.h
new file mode 100644
index 0000000000..5396085d04
--- /dev/null
+++ b/audio/decode/ad.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_AD_H
+#define MPLAYER_AD_H
+
+#include "mpc_info.h"
+#include "libmpdemux/stheader.h"
+
+typedef struct mp_codec_info ad_info_t;
+
+/* interface of video decoder drivers */
+typedef struct ad_functions
+{
+ const ad_info_t *info;
+ int (*preinit)(sh_audio_t *sh);
+ int (*init)(sh_audio_t *sh);
+ void (*uninit)(sh_audio_t *sh);
+ int (*control)(sh_audio_t *sh,int cmd,void* arg, ...);
+ int (*decode_audio)(sh_audio_t *sh, unsigned char *buffer, int minlen,
+ int maxlen);
+} ad_functions_t;
+
+// NULL terminated array of all drivers
+extern const ad_functions_t * const mpcodecs_ad_drivers[];
+
+// fallback if ADCTRL_RESYNC not implemented: sh_audio->a_in_buffer_len=0;
+#define ADCTRL_RESYNC_STREAM 1 // resync, called after seeking
+
+// fallback if ADCTRL_SKIP not implemented: ds_fill_buffer(sh_audio->ds);
+#define ADCTRL_SKIP_FRAME 2 // skip block/frame, called while seeking
+
+// fallback if ADCTRL_QUERY_FORMAT not implemented: sh_audio->sample_format
+#define ADCTRL_QUERY_FORMAT 3 // test for availabilty of a format
+
+// fallback: use hw mixer in libao
+#define ADCTRL_SET_VOLUME 4 // not used at the moment
+
+#endif /* MPLAYER_AD_H */
diff --git a/audio/decode/ad_dvdpcm.c b/audio/decode/ad_dvdpcm.c
new file mode 100644
index 0000000000..41f6a1426d
--- /dev/null
+++ b/audio/decode/ad_dvdpcm.c
@@ -0,0 +1,162 @@
+/*
+ * 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 "config.h"
+#include "mp_msg.h"
+#include "ad_internal.h"
+
+static const ad_info_t info =
+{
+ "Uncompressed DVD/VOB LPCM audio decoder",
+ "dvdpcm",
+ "Nick Kurshev",
+ "A'rpi",
+ ""
+};
+
+LIBAD_EXTERN(dvdpcm)
+
+static int init(sh_audio_t *sh)
+{
+/* DVD PCM Audio:*/
+ sh->i_bps = 0;
+ if(sh->codecdata_len==3){
+ // we have LPCM header:
+ unsigned char h=sh->codecdata[1];
+ sh->channels=1+(h&7);
+ switch((h>>4)&3){
+ case 0: sh->samplerate=48000;break;
+ case 1: sh->samplerate=96000;break;
+ case 2: sh->samplerate=44100;break;
+ case 3: sh->samplerate=32000;break;
+ }
+ switch ((h >> 6) & 3) {
+ case 0:
+ sh->sample_format = AF_FORMAT_S16_BE;
+ sh->samplesize = 2;
+ break;
+ case 1:
+ mp_tmsg(MSGT_DECAUDIO, MSGL_INFO, "Samples of this format are needed to improve support. Please contact the developers.\n");
+ sh->i_bps = sh->channels * sh->samplerate * 5 / 2;
+ case 2:
+ sh->sample_format = AF_FORMAT_S24_BE;
+ sh->samplesize = 3;
+ break;
+ default:
+ sh->sample_format = AF_FORMAT_S16_BE;
+ sh->samplesize = 2;
+ }
+ } else {
+ // use defaults:
+ sh->channels=2;
+ sh->samplerate=48000;
+ sh->sample_format = AF_FORMAT_S16_BE;
+ sh->samplesize = 2;
+ }
+ if (!sh->i_bps)
+ sh->i_bps = sh->samplesize * sh->channels * sh->samplerate;
+ return 1;
+}
+
+static int preinit(sh_audio_t *sh)
+{
+ sh->audio_out_minsize=2048;
+ return 1;
+}
+
+static void uninit(sh_audio_t *sh)
+{
+}
+
+static int control(sh_audio_t *sh,int cmd,void* arg, ...)
+{
+ int skip;
+ switch(cmd)
+ {
+ case ADCTRL_SKIP_FRAME:
+ skip=sh->i_bps/16;
+ skip=skip&(~3);
+ demux_read_data(sh->ds,NULL,skip);
+ return CONTROL_TRUE;
+ }
+ return CONTROL_UNKNOWN;
+}
+
+static int decode_audio(sh_audio_t *sh_audio,unsigned char *buf,int minlen,int maxlen)
+{
+ int j,len;
+ if (sh_audio->samplesize == 3) {
+ if (((sh_audio->codecdata[1] >> 6) & 3) == 1) {
+ // 20 bit
+ // not sure if the "& 0xf0" and "<< 4" are the right way around
+ // can somebody clarify?
+ for (j = 0; j < minlen; j += 12) {
+ char tmp[10];
+ len = demux_read_data(sh_audio->ds, tmp, 10);
+ if (len < 10) break;
+ // first sample
+ buf[j + 0] = tmp[0];
+ buf[j + 1] = tmp[1];
+ buf[j + 2] = tmp[8] & 0xf0;
+ // second sample
+ buf[j + 3] = tmp[2];
+ buf[j + 4] = tmp[3];
+ buf[j + 5] = tmp[8] << 4;
+ // third sample
+ buf[j + 6] = tmp[4];
+ buf[j + 7] = tmp[5];
+ buf[j + 8] = tmp[9] & 0xf0;
+ // fourth sample
+ buf[j + 9] = tmp[6];
+ buf[j + 10] = tmp[7];
+ buf[j + 11] = tmp[9] << 4;
+ }
+ len = j;
+ } else {
+ // 24 bit
+ for (j = 0; j < minlen; j += 12) {
+ char tmp[12];
+ len = demux_read_data(sh_audio->ds, tmp, 12);
+ if (len < 12) break;
+ // first sample
+ buf[j + 0] = tmp[0];
+ buf[j + 1] = tmp[1];
+ buf[j + 2] = tmp[8];
+ // second sample
+ buf[j + 3] = tmp[2];
+ buf[j + 4] = tmp[3];
+ buf[j + 5] = tmp[9];
+ // third sample
+ buf[j + 6] = tmp[4];
+ buf[j + 7] = tmp[5];
+ buf[j + 8] = tmp[10];
+ // fourth sample
+ buf[j + 9] = tmp[6];
+ buf[j + 10] = tmp[7];
+ buf[j + 11] = tmp[11];
+ }
+ len = j;
+ }
+ } else
+ len=demux_read_data(sh_audio->ds,buf,(minlen+3)&(~3));
+ return len;
+}
diff --git a/audio/decode/ad_internal.h b/audio/decode/ad_internal.h
new file mode 100644
index 0000000000..4cffc95126
--- /dev/null
+++ b/audio/decode/ad_internal.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_AD_INTERNAL_H
+#define MPLAYER_AD_INTERNAL_H
+
+#include "codec-cfg.h"
+#include "libaf/format.h"
+
+#include "stream/stream.h"
+#include "libmpdemux/demuxer.h"
+#include "libmpdemux/stheader.h"
+
+#include "ad.h"
+
+static int init(sh_audio_t *sh);
+static int preinit(sh_audio_t *sh);
+static void uninit(sh_audio_t *sh);
+static int control(sh_audio_t *sh,int cmd,void* arg, ...);
+static int decode_audio(sh_audio_t *sh,unsigned char *buffer,int minlen,int maxlen);
+
+#define LIBAD_EXTERN(x) const ad_functions_t mpcodecs_ad_##x = {\
+ &info,\
+ preinit,\
+ init,\
+ uninit,\
+ control,\
+ decode_audio\
+};
+
+#endif /* MPLAYER_AD_INTERNAL_H */
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
new file mode 100644
index 0000000000..2eacfadb8f
--- /dev/null
+++ b/audio/decode/ad_lavc.c
@@ -0,0 +1,413 @@
+/*
+ * 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 <stdbool.h>
+#include <assert.h>
+
+#include <libavcodec/avcodec.h>
+#include <libavutil/opt.h>
+
+#include "talloc.h"
+
+#include "config.h"
+#include "mp_msg.h"
+#include "options.h"
+
+#include "ad_internal.h"
+#include "libaf/reorder_ch.h"
+
+#include "mpbswap.h"
+
+static const ad_info_t info =
+{
+ "libavcodec audio decoders",
+ "ffmpeg",
+ "",
+ "",
+ "",
+ .print_name = "libavcodec",
+};
+
+LIBAD_EXTERN(ffmpeg)
+
+struct priv {
+ AVCodecContext *avctx;
+ AVFrame *avframe;
+ char *output;
+ char *output_packed; // used by deplanarize to store packed audio samples
+ int output_left;
+ int unitsize;
+ int previous_data_left; // input demuxer packet data
+};
+
+static int preinit(sh_audio_t *sh)
+{
+ return 1;
+}
+
+/* Prefer playing audio with the samplerate given in container data
+ * if available, but take number the number of channels and sample format
+ * from the codec, since if the codec isn't using the correct values for
+ * those everything breaks anyway.
+ */
+static int setup_format(sh_audio_t *sh_audio,
+ const AVCodecContext *lavc_context)
+{
+ int sample_format = sh_audio->sample_format;
+ switch (av_get_packed_sample_fmt(lavc_context->sample_fmt)) {
+ case AV_SAMPLE_FMT_U8: sample_format = AF_FORMAT_U8; break;
+ case AV_SAMPLE_FMT_S16: sample_format = AF_FORMAT_S16_NE; break;
+ case AV_SAMPLE_FMT_S32: sample_format = AF_FORMAT_S32_NE; break;
+ case AV_SAMPLE_FMT_FLT: sample_format = AF_FORMAT_FLOAT_NE; break;
+ default:
+ mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Unsupported sample format\n");
+ sample_format = AF_FORMAT_UNKNOWN;
+ }
+
+ bool broken_srate = false;
+ int samplerate = lavc_context->sample_rate;
+ int container_samplerate = sh_audio->container_out_samplerate;
+ if (!container_samplerate && sh_audio->wf)
+ container_samplerate = sh_audio->wf->nSamplesPerSec;
+ if (lavc_context->codec_id == CODEC_ID_AAC
+ && samplerate == 2 * container_samplerate)
+ broken_srate = true;
+ else if (container_samplerate)
+ samplerate = container_samplerate;
+
+ if (lavc_context->channels != sh_audio->channels ||
+ samplerate != sh_audio->samplerate ||
+ sample_format != sh_audio->sample_format) {
+ sh_audio->channels = lavc_context->channels;
+ sh_audio->samplerate = samplerate;
+ sh_audio->sample_format = sample_format;
+ sh_audio->samplesize = af_fmt2bits(sh_audio->sample_format) / 8;
+ if (broken_srate)
+ mp_msg(MSGT_DECAUDIO, MSGL_WARN,
+ "Ignoring broken container sample rate for AAC with SBR\n");
+ return 1;
+ }
+ return 0;
+}
+
+static int init(sh_audio_t *sh_audio)
+{
+ struct MPOpts *opts = sh_audio->opts;
+ AVCodecContext *lavc_context;
+ AVCodec *lavc_codec;
+
+ if (sh_audio->codec->dll) {
+ lavc_codec = avcodec_find_decoder_by_name(sh_audio->codec->dll);
+ if (!lavc_codec) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_ERR,
+ "Cannot find codec '%s' in libavcodec...\n",
+ sh_audio->codec->dll);
+ return 0;
+ }
+ } else if (!sh_audio->libav_codec_id) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_INFO, "No Libav codec ID known. "
+ "Generic lavc decoder is not applicable.\n");
+ return 0;
+ } else {
+ lavc_codec = avcodec_find_decoder(sh_audio->libav_codec_id);
+ if (!lavc_codec) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_INFO, "Libavcodec has no decoder "
+ "for this codec\n");
+ return 0;
+ }
+ }
+
+ sh_audio->codecname = lavc_codec->long_name;
+ if (!sh_audio->codecname)
+ sh_audio->codecname = lavc_codec->name;
+
+ struct priv *ctx = talloc_zero(NULL, struct priv);
+ sh_audio->context = ctx;
+ lavc_context = avcodec_alloc_context3(lavc_codec);
+ ctx->avctx = lavc_context;
+ ctx->avframe = avcodec_alloc_frame();
+
+ // Always try to set - option only exists for AC3 at the moment
+ av_opt_set_double(lavc_context, "drc_scale", opts->drc_level,
+ AV_OPT_SEARCH_CHILDREN);
+ lavc_context->sample_rate = sh_audio->samplerate;
+ lavc_context->bit_rate = sh_audio->i_bps * 8;
+ if (sh_audio->wf) {
+ lavc_context->channels = sh_audio->wf->nChannels;
+ lavc_context->sample_rate = sh_audio->wf->nSamplesPerSec;
+ lavc_context->bit_rate = sh_audio->wf->nAvgBytesPerSec * 8;
+ lavc_context->block_align = sh_audio->wf->nBlockAlign;
+ lavc_context->bits_per_coded_sample = sh_audio->wf->wBitsPerSample;
+ }
+ lavc_context->request_channels = opts->audio_output_channels;
+ lavc_context->codec_tag = sh_audio->format; //FOURCC
+ if (sh_audio->gsh->lavf_codec_tag)
+ lavc_context->codec_tag = sh_audio->gsh->lavf_codec_tag;
+ lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
+ lavc_context->codec_id = lavc_codec->id; // not sure if required, imho not --A'rpi
+
+ /* alloc extra data */
+ if (sh_audio->wf && sh_audio->wf->cbSize > 0) {
+ lavc_context->extradata = av_mallocz(sh_audio->wf->cbSize + FF_INPUT_BUFFER_PADDING_SIZE);
+ lavc_context->extradata_size = sh_audio->wf->cbSize;
+ memcpy(lavc_context->extradata, sh_audio->wf + 1,
+ lavc_context->extradata_size);
+ }
+
+ // for QDM2
+ if (sh_audio->codecdata_len && sh_audio->codecdata &&
+ !lavc_context->extradata) {
+ lavc_context->extradata = av_malloc(sh_audio->codecdata_len +
+ FF_INPUT_BUFFER_PADDING_SIZE);
+ lavc_context->extradata_size = sh_audio->codecdata_len;
+ memcpy(lavc_context->extradata, (char *)sh_audio->codecdata,
+ lavc_context->extradata_size);
+ }
+
+ /* open it */
+ if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Could not open codec.\n");
+ uninit(sh_audio);
+ return 0;
+ }
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "INFO: libavcodec \"%s\" init OK!\n",
+ lavc_codec->name);
+
+ if (sh_audio->format == 0x3343414D) {
+ // MACE 3:1
+ sh_audio->ds->ss_div = 2 * 3; // 1 samples/packet
+ sh_audio->ds->ss_mul = 2 * sh_audio->wf->nChannels; // 1 byte*ch/packet
+ } else if (sh_audio->format == 0x3643414D) {
+ // MACE 6:1
+ sh_audio->ds->ss_div = 2 * 6; // 1 samples/packet
+ sh_audio->ds->ss_mul = 2 * sh_audio->wf->nChannels; // 1 byte*ch/packet
+ }
+
+ // Decode at least 1 byte: (to get header filled)
+ for (int tries = 0;;) {
+ int x = decode_audio(sh_audio, sh_audio->a_buffer, 1,
+ sh_audio->a_buffer_size);
+ if (x > 0) {
+ sh_audio->a_buffer_len = x;
+ break;
+ }
+ if (++tries >= 5) {
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR,
+ "ad_ffmpeg: initial decode failed\n");
+ uninit(sh_audio);
+ return 0;
+ }
+ }
+
+ sh_audio->i_bps = lavc_context->bit_rate / 8;
+ if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
+ sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec;
+
+ switch (av_get_packed_sample_fmt(lavc_context->sample_fmt)) {
+ case AV_SAMPLE_FMT_U8:
+ case AV_SAMPLE_FMT_S16:
+ case AV_SAMPLE_FMT_S32:
+ case AV_SAMPLE_FMT_FLT:
+ break;
+ default:
+ uninit(sh_audio);
+ return 0;
+ }
+ return 1;
+}
+
+static void uninit(sh_audio_t *sh)
+{
+ sh->codecname = NULL;
+ struct priv *ctx = sh->context;
+ if (!ctx)
+ return;
+ AVCodecContext *lavc_context = ctx->avctx;
+
+ if (lavc_context) {
+ if (avcodec_close(lavc_context) < 0)
+ mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Could not close codec.\n");
+ av_freep(&lavc_context->extradata);
+ av_freep(&lavc_context);
+ }
+ avcodec_free_frame(&ctx->avframe);
+ talloc_free(ctx);
+ sh->context = NULL;
+}
+
+static int control(sh_audio_t *sh, int cmd, void *arg, ...)
+{
+ struct priv *ctx = sh->context;
+ switch (cmd) {
+ case ADCTRL_RESYNC_STREAM:
+ avcodec_flush_buffers(ctx->avctx);
+ ds_clear_parser(sh->ds);
+ ctx->previous_data_left = 0;
+ ctx->output_left = 0;
+ return CONTROL_TRUE;
+ }
+ return CONTROL_UNKNOWN;
+}
+
+static av_always_inline void deplanarize(struct sh_audio *sh)
+{
+ struct priv *priv = sh->context;
+
+ size_t bps = av_get_bytes_per_sample(priv->avctx->sample_fmt);
+ size_t nb_samples = priv->avframe->nb_samples;
+ size_t channels = priv->avctx->channels;
+ size_t size = bps * nb_samples * channels;
+
+ if (talloc_get_size(priv->output_packed) != size)
+ priv->output_packed =
+ talloc_realloc_size(priv, priv->output_packed, size);
+
+ size_t offset = 0;
+ unsigned char *output_ptr = priv->output_packed;
+ unsigned char **src = priv->avframe->data;
+
+ for (size_t s = 0; s < nb_samples; s++) {
+ for (size_t c = 0; c < channels; c++) {
+ memcpy(output_ptr, src[c] + offset, bps);
+ output_ptr += bps;
+ }
+ offset += bps;
+ }
+
+ priv->output = priv->output_packed;
+}
+
+static int decode_new_packet(struct sh_audio *sh)
+{
+ struct priv *priv = sh->context;
+ AVCodecContext *avctx = priv->avctx;
+ double pts = MP_NOPTS_VALUE;
+ int insize;
+ bool packet_already_used = priv->previous_data_left;
+ struct demux_packet *mpkt = ds_get_packet2(sh->ds,
+ priv->previous_data_left);
+ unsigned char *start;
+ if (!mpkt) {
+ assert(!priv->previous_data_left);
+ start = NULL;
+ insize = 0;
+ ds_parse(sh->ds, &start, &insize, pts, 0);
+ if (insize <= 0)
+ return -1; // error or EOF
+ } else {
+ assert(mpkt->len >= priv->previous_data_left);
+ if (!priv->previous_data_left) {
+ priv->previous_data_left = mpkt->len;
+ pts = mpkt->pts;
+ }
+ insize = priv->previous_data_left;
+ start = mpkt->buffer + mpkt->len - priv->previous_data_left;
+ int consumed = ds_parse(sh->ds, &start, &insize, pts, 0);
+ priv->previous_data_left -= consumed;
+ priv->previous_data_left = FFMAX(priv->previous_data_left, 0);
+ }
+
+ AVPacket pkt;
+ av_init_packet(&pkt);
+ pkt.data = start;
+ pkt.size = insize;
+ if (mpkt && mpkt->avpacket) {
+ pkt.side_data = mpkt->avpacket->side_data;
+ pkt.side_data_elems = mpkt->avpacket->side_data_elems;
+ }
+ if (pts != MP_NOPTS_VALUE && !packet_already_used) {
+ sh->pts = pts;
+ sh->pts_bytes = 0;
+ }
+ int got_frame = 0;
+ int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt);
+ // LATM may need many packets to find mux info
+ if (ret == AVERROR(EAGAIN))
+ return 0;
+ if (ret < 0) {
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "lavc_audio: error\n");
+ return -1;
+ }
+ // The "insize >= ret" test is sanity check against decoder overreads
+ if (!sh->parser && insize >= ret)
+ priv->previous_data_left = insize - ret;
+ if (!got_frame)
+ return 0;
+ uint64_t unitsize = (uint64_t)av_get_bytes_per_sample(avctx->sample_fmt) *
+ avctx->channels;
+ if (unitsize > 100000)
+ abort();
+ priv->unitsize = unitsize;
+ uint64_t output_left = unitsize * priv->avframe->nb_samples;
+ if (output_left > 500000000)
+ abort();
+ priv->output_left = output_left;
+ if (av_sample_fmt_is_planar(avctx->sample_fmt) && avctx->channels > 1) {
+ deplanarize(sh);
+ } else {
+ priv->output = priv->avframe->data[0];
+ }
+ mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d \n", insize,
+ priv->output_left);
+ return 0;
+}
+
+
+static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
+ int maxlen)
+{
+ struct priv *priv = sh_audio->context;
+ AVCodecContext *avctx = priv->avctx;
+
+ int len = -1;
+ while (len < minlen) {
+ if (!priv->output_left) {
+ if (decode_new_packet(sh_audio) < 0)
+ break;
+ continue;
+ }
+ if (setup_format(sh_audio, avctx))
+ return len;
+ int size = (minlen - len + priv->unitsize - 1);
+ size -= size % priv->unitsize;
+ size = FFMIN(size, priv->output_left);
+ if (size > maxlen)
+ abort();
+ memcpy(buf, priv->output, size);
+ priv->output += size;
+ priv->output_left -= size;
+ if (avctx->channels >= 5) {
+ int samplesize = av_get_bytes_per_sample(avctx->sample_fmt);
+ reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
+ AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
+ avctx->channels,
+ size / samplesize, samplesize);
+ }
+ if (len < 0)
+ len = size;
+ else
+ len += size;
+ buf += size;
+ maxlen -= size;
+ sh_audio->pts_bytes += size;
+ }
+ return len;
+}
diff --git a/audio/decode/ad_mpg123.c b/audio/decode/ad_mpg123.c
new file mode 100644
index 0000000000..a3ce2cdcf6
--- /dev/null
+++ b/audio/decode/ad_mpg123.c
@@ -0,0 +1,489 @@
+/*
+ * MPEG 1.0/2.0/2.5 audio layer I, II, III decoding with libmpg123
+ *
+ * Copyright (C) 2010-2012 Thomas Orgis <thomas@orgis.org>
+ *
+ * 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 "config.h"
+
+#include "ad_internal.h"
+
+static const ad_info_t info = {
+ "MPEG 1.0/2.0/2.5 layers I, II, III",
+ "mpg123",
+ "Thomas Orgis",
+ "mpg123.org",
+ "High-performance decoder using libmpg123."
+};
+
+LIBAD_EXTERN(mpg123)
+
+/* Reducing the ifdeffery to two main variants:
+ * 1. most compatible to any libmpg123 version
+ * 2. fastest variant with recent libmpg123 (>=1.14)
+ * Running variant 2 on older libmpg123 versions may work in
+ * principle, but is not supported.
+ * So, please leave the check for MPG123_API_VERSION there, m-kay?
+ */
+#include <mpg123.h>
+
+/* Enable faster mode of operation with newer libmpg123, avoiding
+ * unnecessary memcpy() calls. */
+#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION >= 33)
+#define AD_MPG123_FRAMEWISE
+#endif
+
+/* Switch for updating bitrate info of VBR files. Not essential. */
+#define AD_MPG123_MEAN_BITRATE
+
+/* Funny thing, that. I assume I shall use it for selecting mpg123 channels.
+ * Please correct me if I guessed wrong. */
+extern int fakemono;
+
+struct ad_mpg123_context {
+ mpg123_handle *handle;
+#ifdef AD_MPG123_MEAN_BITRATE
+ /* Running mean for bit rate, stream length estimation. */
+ float mean_rate;
+ unsigned int mean_count;
+ /* Time delay for updates. */
+ short delay;
+#endif
+ /* If the stream is actually VBR. */
+ char vbr;
+};
+
+/* This initializes libmpg123 and prepares the handle, including funky
+ * parameters. */
+static int preinit(sh_audio_t *sh)
+{
+ int err, flag;
+ struct ad_mpg123_context *con;
+ /* Assumption: You always call preinit + init + uninit, on every file.
+ * But you stop at preinit in case it fails.
+ * If that is not true, one must ensure not to call mpg123_init / exit
+ * twice in a row. */
+ if (mpg123_init() != MPG123_OK)
+ return 0;
+
+ sh->context = malloc(sizeof(struct ad_mpg123_context));
+ con = sh->context;
+ /* Auto-choice of optimized decoder (first argument NULL). */
+ con->handle = mpg123_new(NULL, &err);
+ if (!con->handle)
+ goto bad_end;
+
+ /* Guessing here: Default value triggers forced upmix of mono to stereo. */
+ flag = fakemono == 0 ? MPG123_FORCE_STEREO :
+ fakemono == 1 ? MPG123_MONO_LEFT :
+ fakemono == 2 ? MPG123_MONO_RIGHT : 0;
+ if (mpg123_param(con->handle, MPG123_ADD_FLAGS, flag, 0.0) != MPG123_OK)
+ goto bad_end;
+
+ /* Basic settings.
+ * Don't spill messages, enable better resync with non-seekable streams.
+ * Give both flags individually without error checking to keep going with
+ * old libmpg123. Generally, it is not fatal if the flags are not
+ * honored */
+ mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0);
+ /* Do not bail out on malformed streams at all.
+ * MPlayer does not handle a decoder throwing the towel on crappy input. */
+ mpg123_param(con->handle, MPG123_RESYNC_LIMIT, -1, 0.0);
+
+ /* Open decisions: Configure libmpg123 to force encoding (or stay open about
+ * library builds that support only float or int32 output), (de)configure
+ * gapless decoding (won't work with seeking in MPlayer, though).
+ * Don't forget to eventually enable ReplayGain/RVA support, too.
+ * Let's try to run with the default for now. */
+
+ /* That would produce floating point output.
+ * You can get 32 and 24 bit ints, even 8 bit via format matrix. */
+ /* mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0.); */
+
+ /* Example for RVA choice (available since libmpg123 1.0.0):
+ mpg123_param(con->handle, MPG123_RVA, MPG123_RVA_MIX, 0.0) */
+
+#ifdef AD_MPG123_FRAMEWISE
+ /* Prevent funky automatic resampling.
+ * This way, we can be sure that one frame will never produce
+ * more than 1152 stereo samples. */
+ mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 0.);
+#else
+ /* Older mpg123 is vulnerable to concatenated streams when gapless cutting
+ * is enabled (will only play the jingle of a badly constructed radio
+ * stream). The versions using framewise decoding are fine with that. */
+ mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_GAPLESS, 0.);
+#endif
+
+ return 1;
+
+ bad_end:
+ if (!con->handle)
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
+ mpg123_plain_strerror(err));
+ else
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 preinit error: %s\n",
+ mpg123_strerror(con->handle));
+
+ if (con->handle)
+ mpg123_delete(con->handle);
+ mpg123_exit();
+ free(sh->context);
+ sh->context = NULL;
+ return 0;
+}
+
+/* Compute bitrate from frame size. */
+static int compute_bitrate(struct mpg123_frameinfo *i)
+{
+ static const int samples_per_frame[4][4] = {
+ {-1, 384, 1152, 1152}, /* MPEG 1 */
+ {-1, 384, 1152, 576}, /* MPEG 2 */
+ {-1, 384, 1152, 576}, /* MPEG 2.5 */
+ {-1, -1, -1, -1}, /* Unknown */
+ };
+ return (int) ((i->framesize + 4) * 8 * i->rate * 0.001 /
+ samples_per_frame[i->version][i->layer] + 0.5);
+}
+
+/* Opted against the header printout from old mp3lib, too much
+ * irrelevant info. This is modelled after the mpg123 app's
+ * standard output line.
+ * If more verbosity is demanded, one can add more detail and
+ * also throw in ID3v2 info which libmpg123 collects anyway. */
+static void print_header_compact(struct mpg123_frameinfo *i)
+{
+ static const char *smodes[5] = {
+ "stereo", "joint-stereo", "dual-channel", "mono", "invalid"
+ };
+ static const char *layers[4] = {
+ "Unknown", "I", "II", "III"
+ };
+ static const char *versions[4] = {
+ "1.0", "2.0", "2.5", "x.x"
+ };
+
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "MPEG %s layer %s, ",
+ versions[i->version], layers[i->layer]);
+ switch (i->vbr) {
+ case MPG123_CBR:
+ if (i->bitrate)
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s", i->bitrate);
+ else
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s (free format)",
+ compute_bitrate(i));
+ break;
+ case MPG123_VBR:
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "VBR");
+ break;
+ case MPG123_ABR:
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "%d kbit/s ABR", i->abr_rate);
+ break;
+ default:
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "???");
+ }
+ mp_msg(MSGT_DECAUDIO, MSGL_V, ", %ld Hz %s\n", i->rate,
+ smodes[i->mode]);
+}
+
+/* This tries to extract a requested amount of decoded data.
+ * Even when you request 0 bytes, it will feed enough input so that
+ * the decoder _could_ have delivered something.
+ * Returns byte count >= 0, -1 on error.
+ *
+ * Thoughts on exact pts keeping:
+ * We have to assume that MPEG frames are cut in pieces by packet boundaries.
+ * Also, it might be possible that the first packet does not contain enough
+ * data to ensure initial stream sync... or re-sync on erroneous streams.
+ * So we need something robust to relate the decoded byte count to the correct
+ * time stamp. This is tricky, though. From the outside, you cannot tell if,
+ * after having fed two packets until the first output arrives, one should
+ * start counting from the first packet's pts or the second packet's.
+ * So, let's just count from the last fed package's pts. If the packets are
+ * exactly cut to MPEG frames, this will cause one frame mismatch in the
+ * beginning (when mpg123 peeks ahead for the following header), but will
+ * be corrected with the third frame already. One might add special code to
+ * not increment the base pts past the first packet's after a resync before
+ * the first decoded bytes arrived. */
+static int decode_a_bit(sh_audio_t *sh, unsigned char *buf, int count)
+{
+ int ret = MPG123_OK;
+ int got = 0;
+ struct ad_mpg123_context *con = sh->context;
+
+ /* There will be one MPG123_NEW_FORMAT message on first open.
+ * This will be handled in init(). */
+ do {
+ size_t got_now = 0;
+
+ /* Feed the decoder. This will only fire from the second round on. */
+ if (ret == MPG123_NEED_MORE) {
+ int incount;
+ double pts;
+ unsigned char *inbuf;
+ /* Feed more input data. */
+ incount = ds_get_packet_pts(sh->ds, &inbuf, &pts);
+ if (incount <= 0)
+ break; /* Apparently that's it. EOF. */
+
+ /* Next bytes from that presentation time. */
+ if (pts != MP_NOPTS_VALUE) {
+ sh->pts = pts;
+ sh->pts_bytes = 0;
+ }
+
+#ifdef AD_MPG123_FRAMEWISE
+ /* Have to use mpg123_feed() to avoid decoding here. */
+ ret = mpg123_feed(con->handle, inbuf, incount);
+#else
+ /* Do not use mpg123_feed(), added in later libmpg123 versions. */
+ ret = mpg123_decode(con->handle, inbuf, incount, NULL, 0, NULL);
+#endif
+ if (ret == MPG123_ERR)
+ break;
+ }
+ /* Theoretically, mpg123 could return MPG123_DONE, so be prepared.
+ * Should not happen in our usage, but it is a valid return code. */
+ else if (ret == MPG123_ERR || ret == MPG123_DONE)
+ break;
+
+ /* Try to decode a bit. This is the return value that counts
+ * for the loop condition. */
+#ifdef AD_MPG123_FRAMEWISE
+ if (!buf) { /* fake call just for feeding to get format */
+ ret = mpg123_getformat(con->handle, NULL, NULL, NULL);
+ } else { /* This is the decoding. One frame at a time. */
+ ret = mpg123_replace_buffer(con->handle, buf, count);
+ if (ret == MPG123_OK)
+ ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now);
+ }
+#else
+ ret = mpg123_decode(con->handle, NULL, 0, buf + got, count - got,
+ &got_now);
+#endif
+
+ got += got_now;
+ sh->pts_bytes += got_now;
+
+#ifdef AD_MPG123_FRAMEWISE
+ } while (ret == MPG123_NEED_MORE || (got == 0 && count != 0));
+#else
+ } while (ret == MPG123_NEED_MORE || got < count);
+#endif
+
+ if (ret == MPG123_ERR) {
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 decoding failed: %s\n",
+ mpg123_strerror(con->handle));
+ mpg123_close(con->handle);
+ return -1;
+ }
+
+ return got;
+}
+
+/* Close, reopen stream. Feed data until we know the format of the stream.
+ * 1 on success, 0 on error */
+static int reopen_stream(sh_audio_t *sh)
+{
+ struct ad_mpg123_context *con = (struct ad_mpg123_context*) sh->context;
+
+ mpg123_close(con->handle);
+ /* No resetting of the context:
+ * We do not want to loose the mean bitrate data. */
+
+ /* Open and make sure we have fed enough data to get stream properties. */
+ if (MPG123_OK == mpg123_open_feed(con->handle) &&
+ /* Feed data until mpg123 is ready (has found stream beginning). */
+ !decode_a_bit(sh, NULL, 0)) {
+ return 1;
+ } else {
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR,
+ "mpg123 failed to reopen stream: %s\n",
+ mpg123_strerror(con->handle));
+ mpg123_close(con->handle);
+ return 0;
+ }
+}
+
+/* Now we really start accessing some data and determining file format.
+ * Paranoia note: The mpg123_close() on errors is not really necessary,
+ * But it ensures that we don't accidentally continue decoding with a
+ * bad state (possibly interpreting the format badly or whatnot). */
+static int init(sh_audio_t *sh)
+{
+ long rate = 0;
+ int channels = 0;
+ int encoding = 0;
+ mpg123_id3v2 *v2;
+ struct mpg123_frameinfo finfo;
+ struct ad_mpg123_context *con = sh->context;
+
+ /* We're open about any output format that libmpg123 will suggest.
+ * Note that a standard build will always default to 16 bit signed and
+ * the native sample rate of the file. */
+ if (MPG123_OK == mpg123_format_all(con->handle) &&
+ reopen_stream(sh) &&
+ MPG123_OK == mpg123_getformat(con->handle, &rate, &channels, &encoding) &&
+ /* Forbid the format to change later on. */
+ MPG123_OK == mpg123_format_none(con->handle) &&
+ MPG123_OK == mpg123_format(con->handle, rate, channels, encoding) &&
+ /* Get MPEG header info. */
+ MPG123_OK == mpg123_info(con->handle, &finfo) &&
+ /* Since we queried format, mpg123 should have read past ID3v2 tags.
+ * We need to decide if printing of UTF-8 encoded text info is wanted. */
+ MPG123_OK == mpg123_id3(con->handle, NULL, &v2)) {
+ /* If we are here, we passed all hurdles. Yay! Extract the info. */
+ print_header_compact(&finfo);
+ /* Do we want to print out the UTF-8 Id3v2 info?
+ if (v2)
+ print_id3v2(v2); */
+
+ /* Have kb/s, want B/s
+ * For VBR, the first frame will be a bad estimate. */
+ sh->i_bps = (finfo.bitrate ? finfo.bitrate : compute_bitrate(&finfo))
+ * 1000 / 8;
+#ifdef AD_MPG123_MEAN_BITRATE
+ con->delay = 1;
+ con->mean_rate = 0.;
+ con->mean_count = 0;
+#endif
+ con->vbr = (finfo.vbr != MPG123_CBR);
+ sh->channels = channels;
+ sh->samplerate = rate;
+ /* Without external force, mpg123 will always choose signed encoding,
+ * and non-16-bit only on builds that don't support it.
+ * Be reminded that it doesn't matter to the MPEG file what encoding
+ * is produced from it. */
+ switch (encoding) {
+ case MPG123_ENC_SIGNED_8:
+ sh->sample_format = AF_FORMAT_S8;
+ sh->samplesize = 1;
+ break;
+ case MPG123_ENC_SIGNED_16:
+ sh->sample_format = AF_FORMAT_S16_NE;
+ sh->samplesize = 2;
+ break;
+ /* To stay compatible with the oldest libmpg123 headers, do not rely
+ * on float and 32 bit encoding symbols being defined.
+ * Those formats came later */
+ case 0x1180: /* MPG123_ENC_SIGNED_32 */
+ sh->sample_format = AF_FORMAT_S32_NE;
+ sh->samplesize = 4;
+ break;
+ case 0x200: /* MPG123_ENC_FLOAT_32 */
+ sh->sample_format = AF_FORMAT_FLOAT_NE;
+ sh->samplesize = 4;
+ break;
+ default:
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR,
+ "Bad encoding from mpg123: %i.\n", encoding);
+ mpg123_close(con->handle);
+ return 0;
+ }
+#ifdef AD_MPG123_FRAMEWISE
+ /* Going to decode directly to MPlayer's memory. It is important
+ * to have MPG123_AUTO_RESAMPLE disabled for the buffer size
+ * being an all-time limit. */
+ sh->audio_out_minsize = 1152 * 2 * sh->samplesize;
+#endif
+
+ return 1;
+ } else {
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR, "mpg123 init error: %s\n",
+ mpg123_strerror(con->handle));
+ mpg123_close(con->handle);
+ return 0;
+ }
+}
+
+static void uninit(sh_audio_t *sh)
+{
+ struct ad_mpg123_context *con = (struct ad_mpg123_context*) sh->context;
+
+ mpg123_close(con->handle);
+ mpg123_delete(con->handle);
+ free(sh->context);
+ sh->context = NULL;
+ mpg123_exit();
+}
+
+#ifdef AD_MPG123_MEAN_BITRATE
+/* Update mean bitrate. This could be dropped if accurate time display
+ * on audio file playback is not desired. */
+static void update_info(sh_audio_t *sh)
+{
+ struct ad_mpg123_context *con = sh->context;
+ if (con->vbr && --con->delay < 1) {
+ struct mpg123_frameinfo finfo;
+ if (MPG123_OK == mpg123_info(con->handle, &finfo)) {
+ if (++con->mean_count > ((unsigned int) -1) / 2)
+ con->mean_count = ((unsigned int) -1) / 4;
+
+ /* Might not be numerically optimal, but works fine enough. */
+ con->mean_rate = ((con->mean_count - 1) * con->mean_rate +
+ finfo.bitrate) / con->mean_count;
+ sh->i_bps = (int) (con->mean_rate * 1000 / 8);
+
+ con->delay = 10;
+ }
+ }
+}
+#endif
+
+static int decode_audio(sh_audio_t *sh, unsigned char *buf, int minlen,
+ int maxlen)
+{
+ int bytes;
+
+ bytes = decode_a_bit(sh, buf, maxlen);
+ if (bytes == 0)
+ return -1; /* EOF */
+
+#ifdef AD_MPG123_MEAN_BITRATE
+ update_info(sh);
+#endif
+ return bytes;
+}
+
+static int control(sh_audio_t *sh, int cmd, void *arg, ...)
+{
+ switch (cmd) {
+ case ADCTRL_RESYNC_STREAM:
+ /* Close/reopen the stream for mpg123 to make sure it doesn't
+ * think that it still knows the exact stream position.
+ * Otherwise, we would have funny effects from the gapless code.
+ * Oh, and it helps to minimize artifacts from jumping in the stream. */
+ if (reopen_stream(sh)) {
+#ifdef AD_MPG123_MEAN_BITRATE
+ update_info(sh);
+#endif
+ return CONTROL_TRUE;
+ } else {
+ /* MPlayer ignores this case! It just keeps on decoding.
+ * So we have to make sure resync never fails ... */
+ mp_msg(MSGT_DECAUDIO, MSGL_ERR,
+ "mpg123 cannot reopen stream for resync.\n");
+ return CONTROL_FALSE;
+ }
+ break;
+ }
+ return CONTROL_UNKNOWN;
+}
diff --git a/audio/decode/ad_pcm.c b/audio/decode/ad_pcm.c
new file mode 100644
index 0000000000..c265dfcd56
--- /dev/null
+++ b/audio/decode/ad_pcm.c
@@ -0,0 +1,220 @@
+/*
+ * 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 <stdbool.h>
+
+#include <libavutil/common.h>
+
+#include "talloc.h"
+#include "config.h"
+#include "ad_internal.h"
+#include "libaf/format.h"
+#include "libaf/reorder_ch.h"
+
+static const ad_info_t info = {
+ "Uncompressed PCM audio decoder",
+ "pcm",
+ "Nick Kurshev",
+ "A'rpi",
+ ""
+};
+
+struct ad_pcm_context {
+ unsigned char *buffer;
+ int buffer_pos;
+ int buffer_len;
+ int buffer_size;
+};
+
+LIBAD_EXTERN(pcm)
+
+static int init(sh_audio_t * sh_audio)
+{
+ WAVEFORMATEX *h = sh_audio->wf;
+ if (!h)
+ return 0;
+ sh_audio->i_bps = h->nAvgBytesPerSec;
+ sh_audio->channels = h->nChannels;
+ sh_audio->samplerate = h->nSamplesPerSec;
+ sh_audio->samplesize = (h->wBitsPerSample + 7) / 8;
+ sh_audio->sample_format = AF_FORMAT_S16_LE; // default
+ switch (sh_audio->format) { /* hardware formats: */
+ case 0x0:
+ case 0x1: // Microsoft PCM
+ case 0xfffe: // Extended
+ switch (sh_audio->samplesize) {
+ case 1: sh_audio->sample_format = AF_FORMAT_U8; break;
+ case 2: sh_audio->sample_format = AF_FORMAT_S16_LE; break;
+ case 3: sh_audio->sample_format = AF_FORMAT_S24_LE; break;
+ case 4: sh_audio->sample_format = AF_FORMAT_S32_LE; break;
+ }
+ break;
+ case 0x3: // IEEE float
+ sh_audio->sample_format = AF_FORMAT_FLOAT_LE;
+ break;
+ case 0x6: sh_audio->sample_format = AF_FORMAT_A_LAW; break;
+ case 0x7: sh_audio->sample_format = AF_FORMAT_MU_LAW; break;
+ case 0x11: sh_audio->sample_format = AF_FORMAT_IMA_ADPCM; break;
+ case 0x50: sh_audio->sample_format = AF_FORMAT_MPEG2; break;
+/* case 0x2000: sh_audio->sample_format=AFMT_AC3; */
+ case 0x20776172: // 'raw '
+ sh_audio->sample_format = AF_FORMAT_S16_BE;
+ if (sh_audio->samplesize == 1)
+ sh_audio->sample_format = AF_FORMAT_U8;
+ break;
+ case 0x736F7774: // 'twos'
+ sh_audio->sample_format = AF_FORMAT_S16_BE;
+ // intended fall-through
+ case 0x74776F73: // 'sowt'
+ if (sh_audio->samplesize == 1)
+ sh_audio->sample_format = AF_FORMAT_S8;
+ break;
+ case 0x32336c66: // 'fl32', bigendian float32
+ case 0x32334C46: // 'FL32', bigendian float32 in aiff
+ sh_audio->sample_format = AF_FORMAT_FLOAT_BE;
+ sh_audio->samplesize = 4;
+ break;
+ case 0x666c3332: // '23lf', little endian float32, MPlayer internal fourCC
+ case 0x6D63706C: // 'lpcm'
+ sh_audio->sample_format = AF_FORMAT_FLOAT_LE;
+ sh_audio->samplesize = 4;
+ break;
+/* case 0x34366c66: // 'fl64', bigendian float64
+ sh_audio->sample_format=AF_FORMAT_FLOAT_BE;
+ sh_audio->samplesize=8;
+ break;
+ case 0x666c3634: // '46lf', little endian float64, MPlayer internal fourCC
+ sh_audio->sample_format=AF_FORMAT_FLOAT_LE;
+ sh_audio->samplesize=8;
+ break;*/
+ case 0x34326e69: // 'in24', bigendian int24
+ sh_audio->sample_format = AF_FORMAT_S24_BE;
+ sh_audio->samplesize = 3;
+ break;
+ case 0x696e3234: // '42ni', little endian int24, MPlayer internal fourCC
+ sh_audio->sample_format = AF_FORMAT_S24_LE;
+ sh_audio->samplesize = 3;
+ break;
+ case 0x32336e69: // 'in32', bigendian int32
+ sh_audio->sample_format = AF_FORMAT_S32_BE;
+ sh_audio->samplesize = 4;
+ break;
+ case 0x696e3332: // '23ni', little endian int32, MPlayer internal fourCC
+ sh_audio->sample_format = AF_FORMAT_S32_LE;
+ sh_audio->samplesize = 4;
+ break;
+ case MKTAG('M', 'P', 'a', 'f'):
+ sh_audio->sample_format = h->wFormatTag;
+ sh_audio->samplesize = (af_fmt2bits(sh_audio->sample_format) + 7) / 8;
+ break;
+ default:
+ if (sh_audio->samplesize != 2)
+ sh_audio->sample_format = AF_FORMAT_U8;
+ }
+ if (!sh_audio->samplesize) // this would cause MPlayer to hang later
+ sh_audio->samplesize = 2;
+ sh_audio->context = talloc_zero(NULL, struct ad_pcm_context);
+ return 1;
+}
+
+static int preinit(sh_audio_t *sh)
+{
+ sh->audio_out_minsize = 2048;
+ return 1;
+}
+
+static void uninit(sh_audio_t *sh)
+{
+ talloc_free(sh->context);
+}
+
+static int control(sh_audio_t *sh, int cmd, void *arg, ...)
+{
+ struct ad_pcm_context *ctx = sh->context;
+ int skip;
+ switch (cmd) {
+ case ADCTRL_RESYNC_STREAM:
+ ctx->buffer_len = 0;
+ return true;
+ case ADCTRL_SKIP_FRAME:
+ skip = sh->i_bps / 16;
+ skip = skip & (~3);
+ demux_read_data(sh->ds, NULL, skip);
+ return CONTROL_TRUE;
+ }
+ return CONTROL_UNKNOWN;
+}
+
+static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
+ int maxlen)
+{
+ int unitsize = sh_audio->channels * sh_audio->samplesize;
+ minlen = (minlen + unitsize - 1) / unitsize * unitsize;
+ if (minlen > maxlen)
+ // if someone needs hundreds of channels adjust audio_out_minsize
+ // based on channels in preinit()
+ return -1;
+
+ int len = 0;
+ struct ad_pcm_context *ctx = sh_audio->context;
+ while (len < minlen) {
+ if (ctx->buffer_len - ctx->buffer_pos <= 0) {
+ double pts;
+ unsigned char *ptr;
+ int plen = ds_get_packet_pts(sh_audio->ds, &ptr, &pts);
+ if (plen < 0)
+ break;
+ if (ctx->buffer_size < plen) {
+ talloc_free(ctx->buffer);
+ ctx->buffer = talloc_size(ctx, plen);
+ ctx->buffer_size = plen;
+ }
+ memcpy(ctx->buffer, ptr, plen);
+ ctx->buffer_len = plen;
+ ctx->buffer_pos = 0;
+ if (pts != MP_NOPTS_VALUE) {
+ sh_audio->pts = pts;
+ sh_audio->pts_bytes = 0;
+ }
+ }
+ int from_stored = ctx->buffer_len - ctx->buffer_pos;
+ if (from_stored > minlen - len)
+ from_stored = minlen - len;
+ memcpy(buf + len, ctx->buffer + ctx->buffer_pos, from_stored);
+ ctx->buffer_pos += from_stored;
+ sh_audio->pts_bytes += from_stored;
+ len += from_stored;
+ }
+ if (len % unitsize) {
+ mp_msg(MSGT_DECAUDIO, MSGL_WARN, "[ad_pcm] discarding partial sample "
+ "at end\n");
+ len -= len % unitsize;
+ }
+ if (len == 0)
+ len = -1; // The loop above only exits at error/EOF
+ if (len > 0 && sh_audio->channels >= 5) {
+ reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
+ AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
+ sh_audio->channels, len / sh_audio->samplesize,
+ sh_audio->samplesize);
+ }
+ return len;
+}
diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c
new file mode 100644
index 0000000000..877bc99317
--- /dev/null
+++ b/audio/decode/ad_spdif.c
@@ -0,0 +1,310 @@
+/*
+ * 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 <string.h>
+
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/opt.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "ad_internal.h"
+
+static const ad_info_t info = {
+ "libavformat/spdifenc audio pass-through decoder.",
+ "spdif",
+ "Naoya OYAMA",
+ "Naoya OYAMA",
+ "For ALL hardware decoders"
+};
+
+LIBAD_EXTERN(spdif)
+
+#define FILENAME_SPDIFENC "spdif"
+#define OUTBUF_SIZE 65536
+struct spdifContext {
+ AVFormatContext *lavf_ctx;
+ int iec61937_packet_size;
+ int out_buffer_len;
+ int out_buffer_size;
+ uint8_t *out_buffer;
+ uint8_t pb_buffer[OUTBUF_SIZE];
+};
+
+static int read_packet(void *p, uint8_t *buf, int buf_size)
+{
+ // spdifenc does not use read callback.
+ return 0;
+}
+
+static int write_packet(void *p, uint8_t *buf, int buf_size)
+{
+ int len;
+ struct spdifContext *ctx = p;
+
+ len = FFMIN(buf_size, ctx->out_buffer_size -ctx->out_buffer_len);
+ memcpy(&ctx->out_buffer[ctx->out_buffer_len], buf, len);
+ ctx->out_buffer_len += len;
+ return len;
+}
+
+static int64_t seek(void *p, int64_t offset, int whence)
+{
+ // spdifenc does not use seek callback.
+ return 0;
+}
+
+static int preinit(sh_audio_t *sh)
+{
+ sh->samplesize = 2;
+ return 1;
+}
+
+static int init(sh_audio_t *sh)
+{
+ int i, x, in_size, srate, bps, *dtshd_rate;
+ unsigned char *start;
+ double pts;
+ static const struct {
+ const char *name; enum CodecID id;
+ } fmt_id_type[] = {
+ { "aac" , CODEC_ID_AAC },
+ { "ac3" , CODEC_ID_AC3 },
+ { "dca" , CODEC_ID_DTS },
+ { "eac3", CODEC_ID_EAC3 },
+ { "mpa" , CODEC_ID_MP3 },
+ { "thd" , CODEC_ID_TRUEHD },
+ { NULL , 0 }
+ };
+ AVFormatContext *lavf_ctx = NULL;
+ AVStream *stream = NULL;
+ const AVOption *opt = NULL;
+ struct spdifContext *spdif_ctx = NULL;
+
+ spdif_ctx = av_mallocz(sizeof(*spdif_ctx));
+ if (!spdif_ctx)
+ goto fail;
+ spdif_ctx->lavf_ctx = avformat_alloc_context();
+ if (!spdif_ctx->lavf_ctx)
+ goto fail;
+
+ sh->context = spdif_ctx;
+ lavf_ctx = spdif_ctx->lavf_ctx;
+
+ lavf_ctx->oformat = av_guess_format(FILENAME_SPDIFENC, NULL, NULL);
+ if (!lavf_ctx->oformat)
+ goto fail;
+ lavf_ctx->priv_data = av_mallocz(lavf_ctx->oformat->priv_data_size);
+ if (!lavf_ctx->priv_data)
+ goto fail;
+ lavf_ctx->pb = avio_alloc_context(spdif_ctx->pb_buffer, OUTBUF_SIZE, 1, spdif_ctx,
+ read_packet, write_packet, seek);
+ if (!lavf_ctx->pb)
+ goto fail;
+ stream = avformat_new_stream(lavf_ctx, 0);
+ if (!stream)
+ goto fail;
+ lavf_ctx->duration = AV_NOPTS_VALUE;
+ lavf_ctx->start_time = AV_NOPTS_VALUE;
+ for (i = 0; fmt_id_type[i].name; i++) {
+ if (!strcmp(sh->codec->dll, fmt_id_type[i].name)) {
+ lavf_ctx->streams[0]->codec->codec_id = fmt_id_type[i].id;
+ break;
+ }
+ }
+ lavf_ctx->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
+ if (AVERROR_PATCHWELCOME == lavf_ctx->oformat->write_header(lavf_ctx)) {
+ mp_msg(MSGT_DECAUDIO,MSGL_INFO,
+ "This codec is not supported by spdifenc.\n");
+ goto fail;
+ }
+
+ // get sample_rate & bitrate from parser
+ bps = srate = 0;
+ x = ds_get_packet_pts(sh->ds, &start, &pts);
+ in_size = x;
+ if (x <= 0) {
+ pts = MP_NOPTS_VALUE;
+ x = 0;
+ }
+ ds_parse(sh->ds, &start, &x, pts, 0);
+ if (x == 0) { // not enough buffer
+ srate = 48000; //fake value
+ bps = 768000/8; //fake value
+ } else if (sh->avctx) {
+ if (sh->avctx->sample_rate < 44100) {
+ mp_msg(MSGT_DECAUDIO,MSGL_INFO,
+ "This stream sample_rate[%d Hz] may be broken. "
+ "Force reset 48000Hz.\n",
+ sh->avctx->sample_rate);
+ srate = 48000; //fake value
+ } else
+ srate = sh->avctx->sample_rate;
+ bps = sh->avctx->bit_rate/8;
+ }
+ sh->ds->buffer_pos -= in_size;
+
+ switch (lavf_ctx->streams[0]->codec->codec_id) {
+ case CODEC_ID_AAC:
+ spdif_ctx->iec61937_packet_size = 16384;
+ sh->sample_format = AF_FORMAT_IEC61937_LE;
+ sh->samplerate = srate;
+ sh->channels = 2;
+ sh->i_bps = bps;
+ break;
+ case CODEC_ID_AC3:
+ spdif_ctx->iec61937_packet_size = 6144;
+ sh->sample_format = AF_FORMAT_IEC61937_LE;
+ sh->samplerate = srate;
+ sh->channels = 2;
+ sh->i_bps = bps;
+ break;
+ case CODEC_ID_DTS: // FORCE USE DTS-HD
+ opt = av_opt_find(&lavf_ctx->oformat->priv_class,
+ "dtshd_rate", NULL, 0, 0);
+ if (!opt)
+ goto fail;
+ dtshd_rate = (int*)(((uint8_t*)lavf_ctx->priv_data) +
+ opt->offset);
+ *dtshd_rate = 192000*4;
+ spdif_ctx->iec61937_packet_size = 32768;
+ sh->sample_format = AF_FORMAT_IEC61937_LE;
+ sh->samplerate = 192000; // DTS core require 48000
+ sh->channels = 2*4;
+ sh->i_bps = bps;
+ break;
+ case CODEC_ID_EAC3:
+ spdif_ctx->iec61937_packet_size = 24576;
+ sh->sample_format = AF_FORMAT_IEC61937_LE;
+ sh->samplerate = 192000;
+ sh->channels = 2;
+ sh->i_bps = bps;
+ break;
+ case CODEC_ID_MP3:
+ spdif_ctx->iec61937_packet_size = 4608;
+ sh->sample_format = AF_FORMAT_MPEG2;
+ sh->samplerate = srate;
+ sh->channels = 2;
+ sh->i_bps = bps;
+ break;
+ case CODEC_ID_TRUEHD:
+ spdif_ctx->iec61937_packet_size = 61440;
+ sh->sample_format = AF_FORMAT_IEC61937_LE;
+ sh->samplerate = 192000;
+ sh->channels = 8;
+ sh->i_bps = bps;
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+
+fail:
+ uninit(sh);
+ return 0;
+}
+
+static int decode_audio(sh_audio_t *sh, unsigned char *buf,
+ int minlen, int maxlen)
+{
+ struct spdifContext *spdif_ctx = sh->context;
+ AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
+ AVPacket pkt;
+ double pts;
+ int ret, in_size, consumed, x;
+ unsigned char *start = NULL;
+
+ consumed = spdif_ctx->out_buffer_len = 0;
+ spdif_ctx->out_buffer_size = maxlen;
+ spdif_ctx->out_buffer = buf;
+ while (spdif_ctx->out_buffer_len + spdif_ctx->iec61937_packet_size < maxlen
+ && spdif_ctx->out_buffer_len < minlen) {
+ if (sh->ds->eof)
+ break;
+ x = ds_get_packet_pts(sh->ds, &start, &pts);
+ if (x <= 0) {
+ x = 0;
+ ds_parse(sh->ds, &start, &x, MP_NOPTS_VALUE, 0);
+ if (x == 0)
+ continue; // END_NOT_FOUND
+ in_size = x;
+ } else {
+ in_size = x;
+ consumed = ds_parse(sh->ds, &start, &x, pts, 0);
+ if (x == 0) {
+ mp_msg(MSGT_DECAUDIO,MSGL_V,
+ "start[%p] in_size[%d] consumed[%d] x[%d].\n",
+ start, in_size, consumed, x);
+ continue; // END_NOT_FOUND
+ }
+ sh->ds->buffer_pos -= in_size - consumed;
+ }
+ av_init_packet(&pkt);
+ pkt.data = start;
+ pkt.size = x;
+ mp_msg(MSGT_DECAUDIO,MSGL_V,
+ "start[%p] pkt.size[%d] in_size[%d] consumed[%d] x[%d].\n",
+ start, pkt.size, in_size, consumed, x);
+ if (pts != MP_NOPTS_VALUE) {
+ sh->pts = pts;
+ sh->pts_bytes = 0;
+ }
+ ret = lavf_ctx->oformat->write_packet(lavf_ctx, &pkt);
+ if (ret < 0)
+ break;
+ }
+ sh->pts_bytes += spdif_ctx->out_buffer_len;
+ return spdif_ctx->out_buffer_len;
+}
+
+static int control(sh_audio_t *sh, int cmd, void* arg, ...)
+{
+ unsigned char *start;
+ double pts;
+
+ switch (cmd) {
+ case ADCTRL_RESYNC_STREAM:
+ case ADCTRL_SKIP_FRAME:
+ ds_get_packet_pts(sh->ds, &start, &pts);
+ return CONTROL_TRUE;
+ }
+ return CONTROL_UNKNOWN;
+}
+
+static void uninit(sh_audio_t *sh)
+{
+ struct spdifContext *spdif_ctx = sh->context;
+ AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
+
+ if (lavf_ctx) {
+ if (lavf_ctx->oformat)
+ lavf_ctx->oformat->write_trailer(lavf_ctx);
+ av_freep(&lavf_ctx->pb);
+ if (lavf_ctx->streams) {
+ av_freep(&lavf_ctx->streams[0]->codec);
+ av_freep(&lavf_ctx->streams[0]->info);
+ av_freep(&lavf_ctx->streams[0]);
+ }
+ av_freep(&lavf_ctx->streams);
+ av_freep(&lavf_ctx->priv_data);
+ }
+ av_freep(&lavf_ctx);
+ av_freep(&spdif_ctx);
+}
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
new file mode 100644
index 0000000000..2602352e52
--- /dev/null
+++ b/audio/decode/dec_audio.c
@@ -0,0 +1,462 @@
+/*
+ * 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 <assert.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "bstr.h"
+
+#include "stream/stream.h"
+#include "libmpdemux/demuxer.h"
+
+#include "codec-cfg.h"
+#include "libmpdemux/stheader.h"
+
+#include "dec_audio.h"
+#include "ad.h"
+#include "libaf/format.h"
+
+#include "libaf/af.h"
+
+int fakemono = 0;
+
+struct af_cfg af_cfg = { 1, NULL }; // Configuration for audio filters
+
+void afm_help(void)
+{
+ int i;
+ mp_tmsg(MSGT_DECAUDIO, MSGL_INFO, "Available (compiled-in) audio codec families/drivers:\n");
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_DRIVERS\n");
+ mp_msg(MSGT_DECAUDIO, MSGL_INFO, " afm: info: (comment)\n");
+ for (i = 0; mpcodecs_ad_drivers[i] != NULL; i++)
+ if (mpcodecs_ad_drivers[i]->info->comment
+ && mpcodecs_ad_drivers[i]->info->comment[0])
+ mp_msg(MSGT_DECAUDIO, MSGL_INFO, "%9s %s (%s)\n",
+ mpcodecs_ad_drivers[i]->info->short_name,
+ mpcodecs_ad_drivers[i]->info->name,
+ mpcodecs_ad_drivers[i]->info->comment);
+ else
+ mp_msg(MSGT_DECAUDIO, MSGL_INFO, "%9s %s\n",
+ mpcodecs_ad_drivers[i]->info->short_name,
+ mpcodecs_ad_drivers[i]->info->name);
+}
+
+static int init_audio_codec(sh_audio_t *sh_audio)
+{
+ assert(!sh_audio->initialized);
+ resync_audio_stream(sh_audio);
+ if ((af_cfg.force & AF_INIT_FORMAT_MASK) == AF_INIT_FLOAT) {
+ int fmt = AF_FORMAT_FLOAT_NE;
+ if (sh_audio->ad_driver->control(sh_audio, ADCTRL_QUERY_FORMAT,
+ &fmt) == CONTROL_TRUE) {
+ sh_audio->sample_format = fmt;
+ sh_audio->samplesize = 4;
+ }
+ }
+ sh_audio->audio_out_minsize = 8192; // default, preinit() may change it
+ if (!sh_audio->ad_driver->preinit(sh_audio)) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "ADecoder preinit failed :(\n");
+ return 0;
+ }
+
+ /* allocate audio in buffer: */
+ if (sh_audio->audio_in_minsize > 0) {
+ sh_audio->a_in_buffer_size = sh_audio->audio_in_minsize;
+ mp_tmsg(MSGT_DECAUDIO, MSGL_V, "dec_audio: Allocating %d bytes for input buffer.\n",
+ sh_audio->a_in_buffer_size);
+ sh_audio->a_in_buffer = av_mallocz(sh_audio->a_in_buffer_size);
+ }
+
+ const int base_size = 65536;
+ // At least 64 KiB plus rounding up to next decodable unit size
+ sh_audio->a_buffer_size = base_size + sh_audio->audio_out_minsize;
+
+ mp_tmsg(MSGT_DECAUDIO, MSGL_V, "dec_audio: Allocating %d + %d = %d bytes for output buffer.\n",
+ sh_audio->audio_out_minsize, base_size, sh_audio->a_buffer_size);
+
+ sh_audio->a_buffer = av_mallocz(sh_audio->a_buffer_size);
+ if (!sh_audio->a_buffer)
+ abort();
+ sh_audio->a_buffer_len = 0;
+
+ if (!sh_audio->ad_driver->init(sh_audio)) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_V, "ADecoder init failed :(\n");
+ uninit_audio(sh_audio); // free buffers
+ return 0;
+ }
+
+ sh_audio->initialized = 1;
+
+ if (!sh_audio->channels || !sh_audio->samplerate) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder did not specify "
+ "audio format!\n");
+ uninit_audio(sh_audio); // free buffers
+ return 0;
+ }
+
+ if (!sh_audio->o_bps)
+ sh_audio->o_bps = sh_audio->channels * sh_audio->samplerate
+ * sh_audio->samplesize;
+ return 1;
+}
+
+static int init_audio(sh_audio_t *sh_audio, char *codecname, char *afm,
+ int status, stringset_t *selected)
+{
+ int force = 0;
+ if (codecname && codecname[0] == '+') {
+ codecname = &codecname[1];
+ force = 1;
+ }
+ sh_audio->codec = NULL;
+ while (1) {
+ const ad_functions_t *mpadec;
+ sh_audio->ad_driver = 0;
+ if (!(sh_audio->codec = find_audio_codec(sh_audio->format,
+ NULL,
+ sh_audio->codec, force)))
+ break;
+ // ok we found one codec
+ if (stringset_test(selected, sh_audio->codec->name))
+ continue; // already tried & failed
+ if (codecname && strcmp(sh_audio->codec->name, codecname))
+ continue; // -ac
+ if (afm && strcmp(sh_audio->codec->drv, afm))
+ continue; // afm doesn't match
+ if (!force && sh_audio->codec->status < status)
+ continue; // too unstable
+ stringset_add(selected, sh_audio->codec->name); // tagging it
+ // ok, it matches all rules, let's find the driver!
+ int i;
+ for (i = 0; mpcodecs_ad_drivers[i] != NULL; i++)
+ if (!strcmp(mpcodecs_ad_drivers[i]->info->short_name,
+ sh_audio->codec->drv))
+ break;
+ mpadec = mpcodecs_ad_drivers[i];
+ if (!mpadec) { // driver not available (==compiled in)
+ mp_tmsg(MSGT_DECAUDIO, MSGL_ERR,
+ "Requested audio codec family [%s] (afm=%s) not available.\nEnable it at compilation.\n",
+ sh_audio->codec->name, sh_audio->codec->drv);
+ continue;
+ }
+ // it's available, let's try to init!
+ // init()
+ mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Opening audio decoder: [%s] %s\n",
+ mpadec->info->short_name, mpadec->info->name);
+ sh_audio->ad_driver = mpadec;
+ if (!init_audio_codec(sh_audio)) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_WARN, "Audio decoder init failed for "
+ "codecs.conf entry \"%s\".\n", sh_audio->codec->name);
+ continue; // try next...
+ }
+ // Yeah! We got it!
+ return 1;
+ }
+ return 0;
+}
+
+int init_best_audio_codec(sh_audio_t *sh_audio, char **audio_codec_list,
+ char **audio_fm_list)
+{
+ stringset_t selected;
+ char *ac_l_default[2] = { "", (char *) NULL };
+ // hack:
+ if (!audio_codec_list)
+ audio_codec_list = ac_l_default;
+ // Go through the codec.conf and find the best codec...
+ sh_audio->initialized = 0;
+ stringset_init(&selected);
+ while (!sh_audio->initialized && *audio_codec_list) {
+ char *audio_codec = *(audio_codec_list++);
+ if (audio_codec[0]) {
+ if (audio_codec[0] == '-') {
+ // disable this codec:
+ stringset_add(&selected, audio_codec + 1);
+ } else {
+ // forced codec by name:
+ mp_tmsg(MSGT_DECAUDIO, MSGL_INFO, "Forced audio codec: %s\n",
+ audio_codec);
+ init_audio(sh_audio, audio_codec, NULL, -1, &selected);
+ }
+ } else {
+ int status;
+ // try in stability order: UNTESTED, WORKING, BUGGY.
+ // never try CRASHING.
+ if (audio_fm_list) {
+ char **fmlist = audio_fm_list;
+ // try first the preferred codec families:
+ while (!sh_audio->initialized && *fmlist) {
+ char *audio_fm = *(fmlist++);
+ mp_tmsg(MSGT_DECAUDIO, MSGL_INFO, "Trying to force audio codec driver family %s...\n",
+ audio_fm);
+ for (status = CODECS_STATUS__MAX;
+ status >= CODECS_STATUS__MIN; --status)
+ if (init_audio(sh_audio, NULL, audio_fm, status, &selected))
+ break;
+ }
+ }
+ if (!sh_audio->initialized)
+ for (status = CODECS_STATUS__MAX; status >= CODECS_STATUS__MIN;
+ --status)
+ if (init_audio(sh_audio, NULL, NULL, status, &selected))
+ break;
+ }
+ }
+ stringset_free(&selected);
+
+ if (!sh_audio->initialized) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Cannot find codec for audio format 0x%X.\n",
+ sh_audio->format);
+ return 0; // failed
+ }
+
+ mp_tmsg(MSGT_DECAUDIO, MSGL_INFO, "Selected audio codec: %s [%s]\n",
+ sh_audio->codecname ? sh_audio->codecname : sh_audio->codec->info,
+ sh_audio->ad_driver->info->print_name ?
+ sh_audio->ad_driver->info->print_name :
+ sh_audio->ad_driver->info->short_name);
+ mp_tmsg(MSGT_DECAUDIO, MSGL_V,
+ "Audio codecs.conf entry: %s (%s) afm: %s\n",
+ sh_audio->codec->name, sh_audio->codec->info, sh_audio->codec->drv);
+ mp_msg(MSGT_DECAUDIO, MSGL_V,
+ "AUDIO: %d Hz, %d ch, %s, %3.1f kbit/%3.2f%% (ratio: %d->%d)\n",
+ sh_audio->samplerate, sh_audio->channels,
+ af_fmt2str_short(sh_audio->sample_format),
+ sh_audio->i_bps * 8 * 0.001,
+ ((float) sh_audio->i_bps / sh_audio->o_bps) * 100.0,
+ sh_audio->i_bps, sh_audio->o_bps);
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO,
+ "ID_AUDIO_BITRATE=%d\nID_AUDIO_RATE=%d\n" "ID_AUDIO_NCH=%d\n",
+ sh_audio->i_bps * 8, sh_audio->samplerate, sh_audio->channels);
+
+ return 1; // success
+}
+
+void uninit_audio(sh_audio_t *sh_audio)
+{
+ if (sh_audio->afilter) {
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
+ af_uninit(sh_audio->afilter);
+ free(sh_audio->afilter);
+ sh_audio->afilter = NULL;
+ }
+ if (sh_audio->initialized) {
+ mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Uninit audio: %s\n",
+ sh_audio->codec->drv);
+ sh_audio->ad_driver->uninit(sh_audio);
+ sh_audio->initialized = 0;
+ }
+ av_freep(&sh_audio->a_buffer);
+ av_freep(&sh_audio->a_in_buffer);
+}
+
+
+int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
+ int *out_samplerate, int *out_channels, int *out_format)
+{
+ struct af_stream *afs = sh_audio->afilter;
+ if (!afs) {
+ afs = calloc(1, sizeof(struct af_stream));
+ afs->opts = sh_audio->opts;
+ }
+ // input format: same as codec's output format:
+ afs->input.rate = in_samplerate;
+ afs->input.nch = sh_audio->channels;
+ afs->input.format = sh_audio->sample_format;
+ af_fix_parameters(&(afs->input));
+
+ // output format: same as ao driver's input format (if missing, fallback to input)
+ afs->output.rate = *out_samplerate;
+ afs->output.nch = *out_channels;
+ afs->output.format = *out_format;
+ af_fix_parameters(&(afs->output));
+
+ // filter config:
+ memcpy(&afs->cfg, &af_cfg, sizeof(struct af_cfg));
+
+ mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Building audio filter chain for %dHz/%dch/%s -> %dHz/%dch/%s...\n",
+ afs->input.rate, afs->input.nch,
+ af_fmt2str_short(afs->input.format), afs->output.rate,
+ afs->output.nch, af_fmt2str_short(afs->output.format));
+
+ // let's autoprobe it!
+ if (0 != af_init(afs)) {
+ sh_audio->afilter = NULL;
+ free(afs);
+ return 0; // failed :(
+ }
+
+ *out_samplerate = afs->output.rate;
+ *out_channels = afs->output.nch;
+ *out_format = afs->output.format;
+
+ // ok!
+ sh_audio->afilter = (void *) afs;
+ return 1;
+}
+
+static void set_min_out_buffer_size(struct bstr *outbuf, int len)
+{
+ size_t oldlen = talloc_get_size(outbuf->start);
+ if (oldlen < len) {
+ assert(outbuf->start); // talloc context should be already set
+ mp_msg(MSGT_DECAUDIO, MSGL_V, "Increasing filtered audio buffer size "
+ "from %zd to %d\n", oldlen, len);
+ outbuf->start = talloc_realloc_size(NULL, outbuf->start, len);
+ }
+}
+
+static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
+{
+ assert(len-1 + sh->audio_out_minsize <= sh->a_buffer_size);
+
+ int error = 0;
+
+ // Decode more bytes if needed
+ int old_samplerate = sh->samplerate;
+ int old_channels = sh->channels;
+ int old_sample_format = sh->sample_format;
+ while (sh->a_buffer_len < len) {
+ unsigned char *buf = sh->a_buffer + sh->a_buffer_len;
+ int minlen = len - sh->a_buffer_len;
+ int maxlen = sh->a_buffer_size - sh->a_buffer_len;
+ int ret = sh->ad_driver->decode_audio(sh, buf, minlen, maxlen);
+ int format_change = sh->samplerate != old_samplerate
+ || sh->channels != old_channels
+ || sh->sample_format != old_sample_format;
+ if (ret <= 0 || format_change) {
+ error = format_change ? -2 : -1;
+ // samples from format-changing call get discarded too
+ len = sh->a_buffer_len;
+ break;
+ }
+ sh->a_buffer_len += ret;
+ }
+
+ // Filter
+ struct mp_audio filter_input = {
+ .audio = sh->a_buffer,
+ .len = len,
+ .rate = sh->samplerate,
+ .nch = sh->channels,
+ .format = sh->sample_format
+ };
+ af_fix_parameters(&filter_input);
+ struct mp_audio *filter_output = af_play(sh->afilter, &filter_input);
+ if (!filter_output)
+ return -1;
+ set_min_out_buffer_size(outbuf, outbuf->len + filter_output->len);
+ memcpy(outbuf->start + outbuf->len, filter_output->audio,
+ filter_output->len);
+ outbuf->len += filter_output->len;
+
+ // remove processed data from decoder buffer:
+ sh->a_buffer_len -= len;
+ memmove(sh->a_buffer, sh->a_buffer + len, sh->a_buffer_len);
+
+ return error;
+}
+
+/* Try to get at least minlen decoded+filtered bytes in outbuf
+ * (total length including possible existing data).
+ * Return 0 on success, -1 on error/EOF (not distinguished).
+ * In the former case outbuf->len is always >= minlen on return.
+ * In case of EOF/error it might or might not be.
+ * Outbuf.start must be talloc-allocated, and will be reallocated
+ * if needed to fit all filter output. */
+int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen)
+{
+ // Indicates that a filter seems to be buffering large amounts of data
+ int huge_filter_buffer = 0;
+ // Decoded audio must be cut at boundaries of this many bytes
+ int unitsize = sh_audio->channels * sh_audio->samplesize * 16;
+
+ /* Filter output size will be about filter_multiplier times input size.
+ * If some filter buffers audio in big blocks this might only hold
+ * as average over time. */
+ double filter_multiplier = af_calc_filter_multiplier(sh_audio->afilter);
+
+ /* If the decoder set audio_out_minsize then it can do the equivalent of
+ * "while (output_len < target_len) output_len += audio_out_minsize;",
+ * so we must guarantee there is at least audio_out_minsize-1 bytes
+ * more space in the output buffer than the minimum length we try to
+ * decode. */
+ int max_decode_len = sh_audio->a_buffer_size - sh_audio->audio_out_minsize;
+ if (!unitsize)
+ return -1;
+ max_decode_len -= max_decode_len % unitsize;
+
+ while (outbuf->len < minlen) {
+ int declen = (minlen - outbuf->len) / filter_multiplier
+ + (unitsize << 5); // some extra for possible filter buffering
+ if (huge_filter_buffer)
+ /* Some filter must be doing significant buffering if the estimated
+ * input length didn't produce enough output from filters.
+ * Feed the filters 2k bytes at a time until we have enough output.
+ * Very small amounts could make filtering inefficient while large
+ * amounts can make MPlayer demux the file unnecessarily far ahead
+ * to get audio data and buffer video frames in memory while doing
+ * so. However the performance impact of either is probably not too
+ * significant as long as the value is not completely insane. */
+ declen = 2000;
+ declen -= declen % unitsize;
+ if (declen > max_decode_len)
+ declen = max_decode_len;
+ else
+ /* if this iteration does not fill buffer, we must have lots
+ * of buffering in filters */
+ huge_filter_buffer = 1;
+ int res = filter_n_bytes(sh_audio, outbuf, declen);
+ if (res < 0)
+ return res;
+ }
+ return 0;
+}
+
+void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte)
+{
+ set_min_out_buffer_size(outbuf, outbuf->len + count);
+ memmove(outbuf->start + count, outbuf->start, outbuf->len);
+ memset(outbuf->start, byte, count);
+ outbuf->len += count;
+}
+
+
+void resync_audio_stream(sh_audio_t *sh_audio)
+{
+ sh_audio->a_in_buffer_len = 0; // clear audio input buffer
+ sh_audio->pts = MP_NOPTS_VALUE;
+ if (!sh_audio->initialized)
+ return;
+ sh_audio->ad_driver->control(sh_audio, ADCTRL_RESYNC_STREAM, NULL);
+}
+
+void skip_audio_frame(sh_audio_t *sh_audio)
+{
+ if (!sh_audio->initialized)
+ return;
+ if (sh_audio->ad_driver->control(sh_audio, ADCTRL_SKIP_FRAME, NULL) ==
+ CONTROL_TRUE)
+ return;
+ // default skip code:
+ ds_fill_buffer(sh_audio->ds); // skip block
+}
diff --git a/audio/decode/dec_audio.h b/audio/decode/dec_audio.h
new file mode 100644
index 0000000000..0d4baf0666
--- /dev/null
+++ b/audio/decode/dec_audio.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_DEC_AUDIO_H
+#define MPLAYER_DEC_AUDIO_H
+
+#include "libmpdemux/stheader.h"
+
+struct bstr;
+
+// dec_audio.c:
+void afm_help(void);
+int init_best_audio_codec(sh_audio_t *sh_audio, char** audio_codec_list, char** audio_fm_list);
+int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen);
+void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte);
+void resync_audio_stream(sh_audio_t *sh_audio);
+void skip_audio_frame(sh_audio_t *sh_audio);
+void uninit_audio(sh_audio_t *sh_audio);
+
+int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
+ int *out_samplerate, int *out_channels, int *out_format);
+
+#endif /* MPLAYER_DEC_AUDIO_H */
diff --git a/audio/filter/af.c b/audio/filter/af.c
new file mode 100644
index 0000000000..1f3e446821
--- /dev/null
+++ b/audio/filter/af.c
@@ -0,0 +1,700 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "osdep/strsep.h"
+
+#include "af.h"
+
+// Static list of filters
+extern struct af_info af_info_dummy;
+extern struct af_info af_info_delay;
+extern struct af_info af_info_channels;
+extern struct af_info af_info_format;
+extern struct af_info af_info_resample;
+extern struct af_info af_info_volume;
+extern struct af_info af_info_equalizer;
+extern struct af_info af_info_pan;
+extern struct af_info af_info_surround;
+extern struct af_info af_info_sub;
+extern struct af_info af_info_export;
+extern struct af_info af_info_volnorm;
+extern struct af_info af_info_extrastereo;
+extern struct af_info af_info_lavcac3enc;
+extern struct af_info af_info_lavcresample;
+extern struct af_info af_info_sweep;
+extern struct af_info af_info_hrtf;
+extern struct af_info af_info_ladspa;
+extern struct af_info af_info_center;
+extern struct af_info af_info_sinesuppress;
+extern struct af_info af_info_karaoke;
+extern struct af_info af_info_scaletempo;
+extern struct af_info af_info_bs2b;
+
+static struct af_info* filter_list[]={
+ &af_info_dummy,
+ &af_info_delay,
+ &af_info_channels,
+ &af_info_format,
+ &af_info_resample,
+ &af_info_volume,
+ &af_info_equalizer,
+ &af_info_pan,
+ &af_info_surround,
+ &af_info_sub,
+#ifdef HAVE_SYS_MMAN_H
+ &af_info_export,
+#endif
+ &af_info_volnorm,
+ &af_info_extrastereo,
+ &af_info_lavcac3enc,
+ &af_info_lavcresample,
+ &af_info_sweep,
+ &af_info_hrtf,
+#ifdef CONFIG_LADSPA
+ &af_info_ladspa,
+#endif
+ &af_info_center,
+ &af_info_sinesuppress,
+ &af_info_karaoke,
+ &af_info_scaletempo,
+#ifdef CONFIG_LIBBS2B
+ &af_info_bs2b,
+#endif
+ NULL
+};
+
+// CPU speed
+int* af_cpu_speed = NULL;
+
+/* Find a filter in the static list of filters using it's name. This
+ function is used internally */
+static struct af_info* af_find(char*name)
+{
+ int i=0;
+ while(filter_list[i]){
+ if(!strcmp(filter_list[i]->name,name))
+ return filter_list[i];
+ i++;
+ }
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "Couldn't find audio filter '%s'\n",name);
+ return NULL;
+}
+
+/* Find filter in the dynamic filter list using it's name This
+ function is used for finding already initialized filters */
+struct af_instance* af_get(struct af_stream* s, char* name)
+{
+ struct af_instance* af=s->first;
+ // Find the filter
+ while(af != NULL){
+ if(!strcmp(af->info->name,name))
+ return af;
+ af=af->next;
+ }
+ return NULL;
+}
+
+/*/ Function for creating a new filter of type name. The name may
+ contain the commandline parameters for the filter */
+static struct af_instance* af_create(struct af_stream* s, const char* name_with_cmd)
+{
+ char* name = strdup(name_with_cmd);
+ char* cmdline = name;
+
+ // Allocate space for the new filter and reset all pointers
+ struct af_instance* new=malloc(sizeof(struct af_instance));
+ if (!name || !new) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Could not allocate memory\n");
+ goto err_out;
+ }
+ memset(new,0,sizeof(struct af_instance));
+
+ // Check for commandline parameters
+ strsep(&cmdline, "=");
+
+ // Find filter from name
+ if(NULL == (new->info=af_find(name)))
+ goto err_out;
+
+ /* Make sure that the filter is not already in the list if it is
+ non-reentrant */
+ if(new->info->flags & AF_FLAGS_NOT_REENTRANT){
+ if(af_get(s,name)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one instance of"
+ " the filter '%s' in each stream\n",name);
+ goto err_out;
+ }
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n",name);
+
+ // Initialize the new filter
+ if(AF_OK == new->info->open(new) &&
+ AF_ERROR < new->control(new,AF_CONTROL_POST_CREATE,&s->cfg)){
+ if(cmdline){
+ if(AF_ERROR>=new->control(new,AF_CONTROL_COMMAND_LINE,cmdline))
+ goto err_out;
+ }
+ free(name);
+ return new;
+ }
+
+err_out:
+ free(new);
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Couldn't create or open audio filter '%s'\n",
+ name);
+ free(name);
+ return NULL;
+}
+
+/* Create and insert a new filter of type name before the filter in the
+ argument. This function can be called during runtime, the return
+ value is the new filter */
+static struct af_instance* af_prepend(struct af_stream* s, struct af_instance* af, const char* name)
+{
+ // Create the new filter and make sure it is OK
+ struct af_instance* new=af_create(s,name);
+ if(!new)
+ return NULL;
+ // Update pointers
+ new->next=af;
+ if(af){
+ new->prev=af->prev;
+ af->prev=new;
+ }
+ else
+ s->last=new;
+ if(new->prev)
+ new->prev->next=new;
+ else
+ s->first=new;
+ return new;
+}
+
+/* Create and insert a new filter of type name after the filter in the
+ argument. This function can be called during runtime, the return
+ value is the new filter */
+static struct af_instance* af_append(struct af_stream* s, struct af_instance* af, const char* name)
+{
+ // Create the new filter and make sure it is OK
+ struct af_instance* new=af_create(s,name);
+ if(!new)
+ return NULL;
+ // Update pointers
+ new->prev=af;
+ if(af){
+ new->next=af->next;
+ af->next=new;
+ }
+ else
+ s->first=new;
+ if(new->next)
+ new->next->prev=new;
+ else
+ s->last=new;
+ return new;
+}
+
+// Uninit and remove the filter "af"
+void af_remove(struct af_stream* s, struct af_instance* af)
+{
+ if(!af) return;
+
+ // Print friendly message
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",af->info->name);
+
+ // Notify filter before changing anything
+ af->control(af,AF_CONTROL_PRE_DESTROY,0);
+
+ // Detach pointers
+ if(af->prev)
+ af->prev->next=af->next;
+ else
+ s->first=af->next;
+ if(af->next)
+ af->next->prev=af->prev;
+ else
+ s->last=af->prev;
+
+ // Uninitialize af and free memory
+ af->uninit(af);
+ free(af);
+}
+
+static void print_fmt(struct mp_audio *d)
+{
+ if (d) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%dHz/%dch/%s", d->rate, d->nch,
+ af_fmt2str_short(d->format));
+ } else {
+ mp_msg(MSGT_AFILTER, MSGL_V, "(?)");
+ }
+}
+
+static void af_print_filter_chain(struct af_stream* s)
+{
+ mp_msg(MSGT_AFILTER, MSGL_V, "Audio filter chain:\n");
+
+ mp_msg(MSGT_AFILTER, MSGL_V, " [in] ");
+ print_fmt(&s->input);
+ mp_msg(MSGT_AFILTER, MSGL_V, "\n");
+
+ struct af_instance *af = s->first;
+ while (af) {
+ mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name);
+ print_fmt(af->data);
+ mp_msg(MSGT_AFILTER, MSGL_V, "\n");
+
+ af = af->next;
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, " [out] ");
+ print_fmt(&s->output);
+ mp_msg(MSGT_AFILTER, MSGL_V, "\n");
+}
+
+// Warning:
+// A failed af_reinit() leaves the audio chain behind in a useless, broken
+// state (for example, format filters that were tentatively inserted stay
+// inserted).
+// In that case, you should always rebuild the filter chain, or abort.
+int af_reinit(struct af_stream* s, struct af_instance* af)
+{
+ do{
+ struct mp_audio in; // Format of the input to current filter
+ int rv=0; // Return value
+
+ // Check if there are any filters left in the list
+ if(NULL == af){
+ if(!(af=af_append(s,s->first,"dummy")))
+ return AF_UNKNOWN;
+ else
+ return AF_ERROR;
+ }
+
+ // Check if this is the first filter
+ if(!af->prev)
+ memcpy(&in,&(s->input),sizeof(struct mp_audio));
+ else
+ memcpy(&in,af->prev->data,sizeof(struct mp_audio));
+ // Reset just in case...
+ in.audio=NULL;
+ in.len=0;
+
+ rv = af->control(af,AF_CONTROL_REINIT,&in);
+ switch(rv){
+ case AF_OK:
+ af = af->next;
+ break;
+ case AF_FALSE:{ // Configuration filter is needed
+ // Do auto insertion only if force is not specified
+ if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
+ struct af_instance* new = NULL;
+ // Insert channels filter
+ if((af->prev?af->prev->data->nch:s->input.nch) != in.nch){
+ // Create channels filter
+ if(NULL == (new = af_prepend(s,af,"channels")))
+ return AF_ERROR;
+ // Set number of output channels
+ if(AF_OK != (rv = new->control(new,AF_CONTROL_CHANNELS,&in.nch)))
+ return rv;
+ // Initialize channels filter
+ if(!new->prev)
+ memcpy(&in,&(s->input),sizeof(struct mp_audio));
+ else
+ memcpy(&in,new->prev->data,sizeof(struct mp_audio));
+ if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
+ return rv;
+ }
+ // Insert format filter
+ if((af->prev?af->prev->data->format:s->input.format) != in.format){
+ // Create format filter
+ if(NULL == (new = af_prepend(s,af,"format")))
+ return AF_ERROR;
+ // Set output bits per sample
+ in.format |= af_bits2fmt(in.bps*8);
+ if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT_FMT,&in.format)))
+ return rv;
+ // Initialize format filter
+ if(!new->prev)
+ memcpy(&in,&(s->input),sizeof(struct mp_audio));
+ else
+ memcpy(&in,new->prev->data,sizeof(struct mp_audio));
+ if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
+ return rv;
+ }
+ if(!new){ // Should _never_ happen
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
+ "This error should never occur, please send a bug report.\n");
+ return AF_ERROR;
+ }
+ af=new->next;
+ }
+ else {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Automatic filter insertion disabled "
+ "but formats do not match. Giving up.\n");
+ return AF_ERROR;
+ }
+ break;
+ }
+ case AF_DETACH:{ // Filter is redundant and wants to be unloaded
+ // Do auto remove only if force is not specified
+ if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
+ struct af_instance* aft=af->prev;
+ af_remove(s,af);
+ if(aft)
+ af=aft->next;
+ else
+ af=s->first; // Restart configuration
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not work, audio"
+ " filter '%s' returned error code %i\n",af->info->name,rv);
+ return AF_ERROR;
+ }
+ }while(af);
+
+ af_print_filter_chain(s);
+
+ return AF_OK;
+}
+
+// Uninit and remove all filters
+void af_uninit(struct af_stream* s)
+{
+ while(s->first)
+ af_remove(s,s->first);
+}
+
+/**
+ * Extend the filter chain so we get the required output format at the end.
+ * \return AF_ERROR on error, AF_OK if successful.
+ */
+static int fixup_output_format(struct af_stream* s)
+{
+ struct af_instance* af = NULL;
+ // Check number of output channels fix if not OK
+ // If needed always inserted last -> easy to screw up other filters
+ if(s->output.nch && s->last->data->nch!=s->output.nch){
+ if(!strcmp(s->last->info->name,"format"))
+ af = af_prepend(s,s->last,"channels");
+ else
+ af = af_append(s,s->last,"channels");
+ // Init the new filter
+ if(!af || (AF_OK != af->control(af,AF_CONTROL_CHANNELS,&(s->output.nch))))
+ return AF_ERROR;
+ if(AF_OK != af_reinit(s,af))
+ return AF_ERROR;
+ }
+
+ // Check output format fix if not OK
+ if(s->output.format != AF_FORMAT_UNKNOWN &&
+ s->last->data->format != s->output.format){
+ if(strcmp(s->last->info->name,"format"))
+ af = af_append(s,s->last,"format");
+ else
+ af = s->last;
+ // Init the new filter
+ s->output.format |= af_bits2fmt(s->output.bps*8);
+ if(!af || (AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT,&(s->output.format))))
+ return AF_ERROR;
+ if(AF_OK != af_reinit(s,af))
+ return AF_ERROR;
+ }
+
+ // Re init again just in case
+ if(AF_OK != af_reinit(s,s->first))
+ return AF_ERROR;
+
+ if (s->output.format == AF_FORMAT_UNKNOWN)
+ s->output.format = s->last->data->format;
+ if (!s->output.nch) s->output.nch = s->last->data->nch;
+ if (!s->output.rate) s->output.rate = s->last->data->rate;
+ if((s->last->data->format != s->output.format) ||
+ (s->last->data->nch != s->output.nch) ||
+ (s->last->data->rate != s->output.rate)) {
+ return AF_ERROR;
+ }
+ return AF_OK;
+}
+
+/**
+ * Automatic downmix to stereo in case the codec does not implement it.
+ */
+static void af_downmix(struct af_stream* s)
+{
+ static const char * const downmix_strs[AF_NCH + 1] = {
+ /* FL FR RL RR FC LF AL AR */
+ [3] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0.4",
+ [4] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0:" "0:0.4",
+ [5] = "pan=2:" "0.5:0:" "0:0.5:" "0.2:0:" "0:0.2:" "0.3:0.3",
+ [6] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0.1",
+ [7] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0:" "0:0.1",
+ [8] = "pan=2:" "0.4:0:" "0:0.4:" "0.15:0:" "0:0.15:" "0.25:0.25:" "0.1:0.1:" "0.1:0:" "0:0.1",
+ };
+ const char *af_pan_str = downmix_strs[s->input.nch];
+
+ if (af_pan_str)
+ af_append(s, s->first, af_pan_str);
+}
+
+/* Initialize the stream "s". This function creates a new filter list
+ if necessary according to the values set in input and output. Input
+ and output should contain the format of the current movie and the
+ formate of the preferred output respectively. The function is
+ reentrant i.e. if called with an already initialized stream the
+ stream will be reinitialized.
+ If one of the prefered output parameters is 0 the one that needs
+ no conversion is used (i.e. the output format in the last filter).
+ The return value is 0 if success and -1 if failure */
+int af_init(struct af_stream* s)
+{
+ struct MPOpts *opts = s->opts;
+ int i=0;
+
+ // Sanity check
+ if(!s) return -1;
+
+ // Precaution in case caller is misbehaving
+ s->input.audio = s->output.audio = NULL;
+ s->input.len = s->output.len = 0;
+
+ // Figure out how fast the machine is
+ if(AF_INIT_AUTO == (AF_INIT_TYPE_MASK & s->cfg.force))
+ s->cfg.force = (s->cfg.force & ~AF_INIT_TYPE_MASK) | AF_INIT_TYPE;
+
+ // Check if this is the first call
+ if(!s->first){
+ // Append a downmix pan filter at the beginning of the chain if needed
+ if (s->input.nch != opts->audio_output_channels
+ && opts->audio_output_channels == 2)
+ af_downmix(s);
+ // Add all filters in the list (if there are any)
+ if (s->cfg.list) {
+ while(s->cfg.list[i]){
+ if(!af_append(s,s->last,s->cfg.list[i++]))
+ return -1;
+ }
+ }
+ }
+
+ // If we do not have any filters otherwise
+ // add dummy to make automatic format conversion work
+ if (!s->first && !af_append(s, s->first, "dummy"))
+ return -1;
+
+ // Init filters
+ if(AF_OK != af_reinit(s,s->first))
+ return -1;
+
+ // make sure the chain is not empty and valid (e.g. because of AF_DETACH)
+ if (!s->first)
+ if (!af_append(s,s->first,"dummy") || AF_OK != af_reinit(s,s->first))
+ return -1;
+
+ // Check output format
+ if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
+ struct af_instance* af = NULL; // New filter
+ // Check output frequency if not OK fix with resample
+ if(s->output.rate && s->last->data->rate!=s->output.rate){
+ // try to find a filter that can change samplrate
+ af = af_control_any_rev(s, AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
+ &(s->output.rate));
+ if (!af) {
+ char *resampler = "resample";
+ if ((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW)
+ resampler = "lavcresample";
+ if((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW){
+ if(!strcmp(s->first->info->name,"format"))
+ af = af_append(s,s->first,resampler);
+ else
+ af = af_prepend(s,s->first,resampler);
+ }
+ else{
+ if(!strcmp(s->last->info->name,"format"))
+ af = af_prepend(s,s->last,resampler);
+ else
+ af = af_append(s,s->last,resampler);
+ }
+ // Init the new filter
+ if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
+ &(s->output.rate))))
+ return -1;
+ // Use lin int if the user wants fast
+ if ((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_FAST) {
+ char args[32];
+ sprintf(args, "%d", s->output.rate);
+ if (strcmp(resampler, "lavcresample") == 0)
+ strcat(args, ":1");
+ else
+ strcat(args, ":0:0");
+ af->control(af, AF_CONTROL_COMMAND_LINE, args);
+ }
+ }
+ if(AF_OK != af_reinit(s,af))
+ return -1;
+ }
+ if (AF_OK != fixup_output_format(s)) {
+ // Something is stuffed audio out will not work
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to setup filter system can not"
+ " meet sound-card demands, please send a bug report. \n");
+ af_uninit(s);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* Add filter during execution. This function adds the filter "name"
+ to the stream s. The filter will be inserted somewhere nice in the
+ list of filters. The return value is a pointer to the new filter,
+ If the filter couldn't be added the return value is NULL. */
+struct af_instance* af_add(struct af_stream* s, char* name){
+ struct af_instance* new;
+ // Sanity check
+ if(!s || !s->first || !name)
+ return NULL;
+ // Insert the filter somewhere nice
+ if(!strcmp(s->first->info->name,"format"))
+ new = af_append(s, s->first, name);
+ else
+ new = af_prepend(s, s->first, name);
+ if(!new)
+ return NULL;
+
+ // Reinitalize the filter list
+ if(AF_OK != af_reinit(s, s->first) ||
+ AF_OK != fixup_output_format(s)){
+ while (s->first)
+ af_remove(s, s->first);
+ af_init(s);
+ return NULL;
+ }
+ return new;
+}
+
+// Filter data chunk through the filters in the list
+struct mp_audio* af_play(struct af_stream* s, struct mp_audio* data)
+{
+ struct af_instance* af=s->first;
+ // Iterate through all filters
+ do{
+ if (data->len <= 0) break;
+ data=af->play(af,data);
+ af=af->next;
+ }while(af && data);
+ return data;
+}
+
+/* Calculate the minimum output buffer size for given input data d
+ * when using the RESIZE_LOCAL_BUFFER macro. The +t+1 part ensures the
+ * value is >= len*mul rounded upwards to whole samples even if the
+ * double 'mul' is inexact. */
+int af_lencalc(double mul, struct mp_audio* d)
+{
+ int t = d->bps * d->nch;
+ return d->len * mul + t + 1;
+}
+
+// Calculate average ratio of filter output size to input size
+double af_calc_filter_multiplier(struct af_stream* s)
+{
+ struct af_instance* af=s->first;
+ double mul = 1;
+ // Iterate through all filters and calculate total multiplication factor
+ do{
+ mul *= af->mul;
+ af=af->next;
+ }while(af);
+
+ return mul;
+}
+
+/* Calculate the total delay [bytes output] caused by the filters */
+double af_calc_delay(struct af_stream* s)
+{
+ struct af_instance* af=s->first;
+ register double delay = 0.0;
+ // Iterate through all filters
+ while(af){
+ delay += af->delay;
+ delay *= af->mul;
+ af=af->next;
+ }
+ return delay;
+}
+
+/* Helper function called by the macro with the same name this
+ function should not be called directly */
+int af_resize_local_buffer(struct af_instance* af, struct mp_audio* data)
+{
+ // Calculate new length
+ register int len = af_lencalc(af->mul,data);
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
+ "old len = %i, new len = %i\n",af->info->name,af->data->len,len);
+ // If there is a buffer free it
+ free(af->data->audio);
+ // Create new buffer and check that it is OK
+ af->data->audio = malloc(len);
+ if(!af->data->audio){
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
+ return AF_ERROR;
+ }
+ af->data->len=len;
+ return AF_OK;
+}
+
+// documentation in af.h
+struct af_instance *af_control_any_rev (struct af_stream* s, int cmd, void* arg) {
+ int res = AF_UNKNOWN;
+ struct af_instance* filt = s->last;
+ while (filt) {
+ res = filt->control(filt, cmd, arg);
+ if (res == AF_OK)
+ return filt;
+ filt = filt->prev;
+ }
+ return NULL;
+}
+
+void af_help (void) {
+ int i = 0;
+ mp_msg(MSGT_AFILTER, MSGL_INFO, "Available audio filters:\n");
+ while (filter_list[i]) {
+ if (filter_list[i]->comment && filter_list[i]->comment[0])
+ mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s (%s)\n", filter_list[i]->name, filter_list[i]->info, filter_list[i]->comment);
+ else
+ mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s\n", filter_list[i]->name, filter_list[i]->info);
+ i++;
+ }
+}
+
+void af_fix_parameters(struct mp_audio *data)
+{
+ if (data->nch < 0 || data->nch > AF_NCH) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "Invalid number of channels %i, assuming 2.\n", data->nch);
+ data->nch = 2;
+ }
+ data->bps = af_fmt2bits(data->format)/8;
+}
diff --git a/audio/filter/af.h b/audio/filter/af.h
new file mode 100644
index 0000000000..edce49a978
--- /dev/null
+++ b/audio/filter/af.h
@@ -0,0 +1,349 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_AF_H
+#define MPLAYER_AF_H
+
+#include <stdio.h>
+
+#include "config.h"
+
+#include "options.h"
+#include "libaf/format.h"
+#include "control.h"
+#include "cpudetect.h"
+#include "mp_msg.h"
+
+struct af_instance;
+
+// Number of channels
+#ifndef AF_NCH
+#define AF_NCH 8
+#endif
+
+// Audio data chunk
+struct mp_audio {
+ void *audio; // data buffer
+ int len; // buffer length
+ int rate; // sample rate
+ int nch; // number of channels
+ int format; // format
+ int bps; // bytes per sample
+};
+
+
+// Flags used for defining the behavior of an audio filter
+#define AF_FLAGS_REENTRANT 0x00000000
+#define AF_FLAGS_NOT_REENTRANT 0x00000001
+
+/* Audio filter information not specific for current instance, but for
+ a specific filter */
+struct af_info {
+ const char *info;
+ const char *name;
+ const char *author;
+ const char *comment;
+ const int flags;
+ int (*open)(struct af_instance *vf);
+};
+
+// Linked list of audio filters
+struct af_instance {
+ struct af_info *info;
+ int (*control)(struct af_instance *af, int cmd, void *arg);
+ void (*uninit)(struct af_instance *af);
+ struct mp_audio * (*play)(struct af_instance *af, struct mp_audio *data);
+ void *setup; // setup data for this specific instance and filter
+ struct mp_audio *data; // configuration for outgoing data stream
+ struct af_instance *next;
+ struct af_instance *prev;
+ double delay; /* Delay caused by the filter, in units of bytes read without
+ * corresponding output */
+ double mul; /* length multiplier: how much does this instance change
+ the length of the buffer. */
+};
+
+// Initialization flags
+extern int *af_cpu_speed;
+
+#define AF_INIT_AUTO 0x00000000
+#define AF_INIT_SLOW 0x00000001
+#define AF_INIT_FAST 0x00000002
+#define AF_INIT_FORCE 0x00000003
+#define AF_INIT_TYPE_MASK 0x00000003
+
+#define AF_INIT_INT 0x00000000
+#define AF_INIT_FLOAT 0x00000004
+#define AF_INIT_FORMAT_MASK 0x00000004
+
+// Default init type
+#ifndef AF_INIT_TYPE
+#define AF_INIT_TYPE (af_cpu_speed ? *af_cpu_speed : AF_INIT_SLOW)
+#endif
+
+// Configuration switches
+struct af_cfg {
+ int force; // Initialization type
+ char **list; /* list of names of filters that are added to filter
+ list during first initialization of stream */
+};
+
+// Current audio stream
+struct af_stream {
+ // The first and last filter in the list
+ struct af_instance *first;
+ struct af_instance *last;
+ // Storage for input and output data formats
+ struct mp_audio input;
+ struct mp_audio output;
+ // Configuration for this stream
+ struct af_cfg cfg;
+ struct MPOpts *opts;
+};
+
+/*********************************************
+ // Return values
+ */
+
+#define AF_DETACH 2
+#define AF_OK 1
+#define AF_TRUE 1
+#define AF_FALSE 0
+#define AF_UNKNOWN -1
+#define AF_ERROR -2
+#define AF_FATAL -3
+
+
+
+/*********************************************
+ // Export functions
+ */
+
+/**
+ * \defgroup af_chain Audio filter chain functions
+ * \{
+ * \param s filter chain
+ */
+
+/**
+ * \brief Initialize the stream "s".
+ * \return 0 on success, -1 on failure
+ *
+ * This function creates a new filter list if necessary, according
+ * to the values set in input and output. Input and output should contain
+ * the format of the current movie and the format of the preferred output
+ * respectively.
+ * Filters to convert to the preferred output format are inserted
+ * automatically, except when they are set to 0.
+ * The function is reentrant i.e. if called with an already initialized
+ * stream the stream will be reinitialized.
+ */
+int af_init(struct af_stream *s);
+
+/**
+ * \brief Uninit and remove all filters from audio filter chain
+ */
+void af_uninit(struct af_stream *s);
+
+/**
+ * \brief Reinit the filter list from the given filter on downwards
+ * \param Filter instance to begin the reinit from
+ * \return AF_OK on success or AF_ERROR on failure
+ */
+int af_reinit(struct af_stream *s, struct af_instance *af);
+
+/**
+ * \brief This function adds the filter "name" to the stream s.
+ * \param name name of filter to add
+ * \return pointer to the new filter, NULL if insert failed
+ *
+ * The filter will be inserted somewhere nice in the
+ * list of filters (i.e. at the beginning unless the
+ * first filter is the format filter (why??).
+ */
+struct af_instance *af_add(struct af_stream *s, char *name);
+
+/**
+ * \brief Uninit and remove the filter "af"
+ * \param af filter to remove
+ */
+void af_remove(struct af_stream *s, struct af_instance *af);
+
+/**
+ * \brief find filter in chain by name
+ * \param name name of the filter to find
+ * \return first filter with right name or NULL if not found
+ *
+ * This function is used for finding already initialized filters
+ */
+struct af_instance *af_get(struct af_stream *s, char *name);
+
+/**
+ * \brief filter data chunk through the filters in the list
+ * \param data data to play
+ * \return resulting data
+ * \ingroup af_chain
+ */
+struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data);
+
+/**
+ * \brief send control to all filters, starting with the last until
+ * one accepts the command with AF_OK.
+ * \param cmd filter control command
+ * \param arg argument for filter command
+ * \return the accepting filter or NULL if none was found
+ */
+struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
+
+/**
+ * \brief calculate average ratio of filter output lenth to input length
+ * \return the ratio
+ */
+double af_calc_filter_multiplier(struct af_stream *s);
+
+/**
+ * \brief Calculate the total delay caused by the filters
+ * \return delay in bytes of "missing" output
+ */
+double af_calc_delay(struct af_stream *s);
+
+/** \} */ // end of af_chain group
+
+// Helper functions and macros used inside the audio filters
+
+/**
+ * \defgroup af_filter Audio filter helper functions
+ * \{
+ */
+
+/* Helper function called by the macro with the same name only to be
+ called from inside filters */
+int af_resize_local_buffer(struct af_instance *af, struct mp_audio *data);
+
+/* Helper function used to calculate the exact buffer length needed
+ when buffers are resized. The returned length is >= than what is
+ needed */
+int af_lencalc(double mul, struct mp_audio *data);
+
+/**
+ * \brief convert dB to gain value
+ * \param n number of values to convert
+ * \param in [in] values in dB, <= -200 will become 0 gain
+ * \param out [out] gain values
+ * \param k input values are divided by this
+ * \param mi minimum dB value, input will be clamped to this
+ * \param ma maximum dB value, input will be clamped to this
+ * \return AF_ERROR on error, AF_OK otherwise
+ */
+int af_from_dB(int n, float *in, float *out, float k, float mi, float ma);
+
+/**
+ * \brief convert gain value to dB
+ * \param n number of values to convert
+ * \param in [in] gain values, 0 wil become -200 dB
+ * \param out [out] values in dB
+ * \param k output values will be multiplied by this
+ * \return AF_ERROR on error, AF_OK otherwise
+ */
+int af_to_dB(int n, float *in, float *out, float k);
+
+/**
+ * \brief convert milliseconds to sample time
+ * \param n number of values to convert
+ * \param in [in] values in milliseconds
+ * \param out [out] sample time values
+ * \param rate sample rate
+ * \param mi minimum ms value, input will be clamped to this
+ * \param ma maximum ms value, input will be clamped to this
+ * \return AF_ERROR on error, AF_OK otherwise
+ */
+int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
+
+/**
+ * \brief convert sample time to milliseconds
+ * \param n number of values to convert
+ * \param in [in] sample time values
+ * \param out [out] values in milliseconds
+ * \param rate sample rate
+ * \return AF_ERROR on error, AF_OK otherwise
+ */
+int af_to_ms(int n, int *in, float *out, int rate);
+
+/**
+ * \brief test if output format matches
+ * \param af audio filter
+ * \param out needed format, will be overwritten by available
+ * format if they do not match
+ * \return AF_FALSE if formats do not match, AF_OK if they match
+ *
+ * compares the format, bps, rate and nch values of af->data with out
+ */
+int af_test_output(struct af_instance *af, struct mp_audio *out);
+
+/**
+ * \brief soft clipping function using sin()
+ * \param a input value
+ * \return clipped value
+ */
+float af_softclip(float a);
+
+/** \} */ // end of af_filter group, but more functions of this group below
+
+/** Print a list of all available audio filters */
+void af_help(void);
+
+/**
+ * \brief fill the missing parameters in the struct mp_audio structure
+ * \param data structure to fill
+ * \ingroup af_filter
+ *
+ * Currently only sets bps based on format
+ */
+void af_fix_parameters(struct mp_audio *data);
+
+/** Memory reallocation macro: if a local buffer is used (i.e. if the
+ filter doesn't operate on the incoming buffer this macro must be
+ called to ensure the buffer is big enough.
+ * \ingroup af_filter
+ */
+#define RESIZE_LOCAL_BUFFER(a, d) \
+ ((a->data->len < \
+ af_lencalc(a->mul, d)) ? af_resize_local_buffer(a, d) : AF_OK)
+
+/* Some other useful macro definitions*/
+#ifndef min
+#define min(a, b)(((a) > (b)) ? (b) : (a))
+#endif
+
+#ifndef max
+#define max(a, b)(((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef clamp
+#define clamp(a, min, max) (((a) > (max)) ? (max) : (((a) < (min)) ? (min) : (a)))
+#endif
+
+#ifndef sign
+#define sign(a) (((a) > 0) ? (1) : (-1))
+#endif
+
+#ifndef lrnd
+#define lrnd(a, b) ((b)((a) >= 0.0 ? (a) + 0.5 : (a) - 0.5))
+#endif
+
+#endif /* MPLAYER_AF_H */
diff --git a/audio/filter/af_bs2b.c b/audio/filter/af_bs2b.c
new file mode 100644
index 0000000000..ccbf3794c5
--- /dev/null
+++ b/audio/filter/af_bs2b.c
@@ -0,0 +1,274 @@
+/*
+ * The Bauer stereophonic-to-binaural DSP using bs2b library:
+ * http://bs2b.sourceforge.net/
+ *
+ * Copyright (c) 2009 Andrew Savchenko
+ *
+ * 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 <bs2b.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "af.h"
+#include "subopt-helper.h"
+
+/// Internal specific data of the filter
+struct af_bs2b {
+ int fcut; ///< cut frequency in Hz
+ int feed; ///< feed level for low frequencies in 0.1*dB
+ char *profile; ///< profile (available crossfeed presets)
+ t_bs2bdp filter; ///< instance of a library filter
+};
+
+#define PLAY(name, type) \
+static struct mp_audio *play_##name(struct af_instance *af, struct mp_audio *data) \
+{ \
+ /* filter is called for all pairs of samples available in the buffer */ \
+ bs2b_cross_feed_##name(((struct af_bs2b*)(af->setup))->filter, \
+ (type*)(data->audio), data->len/data->bps/2); \
+\
+ return data; \
+}
+
+PLAY(f, float)
+PLAY(fbe, float)
+PLAY(fle, float)
+PLAY(s32be, int32_t)
+PLAY(u32be, uint32_t)
+PLAY(s32le, int32_t)
+PLAY(u32le, uint32_t)
+PLAY(s24be, bs2b_int24_t)
+PLAY(u24be, bs2b_uint24_t)
+PLAY(s24le, bs2b_int24_t)
+PLAY(u24le, bs2b_uint24_t)
+PLAY(s16be, int16_t)
+PLAY(u16be, uint16_t)
+PLAY(s16le, int16_t)
+PLAY(u16le, uint16_t)
+PLAY(s8, int8_t)
+PLAY(u8, uint8_t)
+
+/// Sanity check for fcut value
+static int test_fcut(void *par)
+{
+ const int val = *(int*)par;
+ if (val >= BS2B_MINFCUT && val <= BS2B_MAXFCUT)
+ return 1;
+
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[bs2b] Cut frequency must be in range [%d..%d], but current value is %d.\n",
+ BS2B_MINFCUT, BS2B_MAXFCUT, val);
+ return 0;
+}
+
+/// Sanity check for feed value
+static int test_feed(void *par)
+{
+ const int val = *(int*)par;
+ if (val >= BS2B_MINFEED && val <= BS2B_MAXFEED)
+ return 1;
+
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[bs2b] Feed level must be in range [%d..%d], but current value is %d.\n",
+ BS2B_MINFEED, BS2B_MAXFEED, val);
+ return 0;
+}
+
+/// Initialization and runtime control
+static int control(struct af_instance *af, int cmd, void *arg)
+{
+ struct af_bs2b *s = af->setup;
+
+ switch (cmd) {
+ case AF_CONTROL_REINIT: {
+ int format;
+ char buf[256];
+ // Sanity check
+ if (!arg) return AF_ERROR;
+
+ format = ((struct mp_audio*)arg)->format;
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = 2; // bs2b is useful only for 2ch audio
+ af->data->bps = ((struct mp_audio*)arg)->bps;
+ af->data->format = format;
+
+ /* check for formats supported by libbs2b
+ and assign corresponding handlers */
+ switch (format) {
+ case AF_FORMAT_FLOAT_BE:
+ af->play = play_fbe;
+ break;
+ case AF_FORMAT_FLOAT_LE:
+ af->play = play_fle;
+ break;
+ case AF_FORMAT_S32_BE:
+ af->play = play_s32be;
+ break;
+ case AF_FORMAT_U32_BE:
+ af->play = play_u32be;
+ break;
+ case AF_FORMAT_S32_LE:
+ af->play = play_s32le;
+ break;
+ case AF_FORMAT_U32_LE:
+ af->play = play_u32le;
+ break;
+ case AF_FORMAT_S24_BE:
+ af->play = play_s24be;
+ break;
+ case AF_FORMAT_U24_BE:
+ af->play = play_u24be;
+ break;
+ case AF_FORMAT_S24_LE:
+ af->play = play_s24le;
+ break;
+ case AF_FORMAT_U24_LE:
+ af->play = play_u24le;
+ break;
+ case AF_FORMAT_S16_BE:
+ af->play = play_s16be;
+ break;
+ case AF_FORMAT_U16_BE:
+ af->play = play_u16be;
+ break;
+ case AF_FORMAT_S16_LE:
+ af->play = play_s16le;
+ break;
+ case AF_FORMAT_U16_LE:
+ af->play = play_u16le;
+ break;
+ case AF_FORMAT_S8:
+ af->play = play_s8;
+ break;
+ case AF_FORMAT_U8:
+ af->play = play_u8;
+ break;
+ default:
+ af->play = play_f;
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ break;
+ }
+
+ // bs2b have srate limits, try to resample if needed
+ if (af->data->rate > BS2B_MAXSRATE || af->data->rate < BS2B_MINSRATE) {
+ af->data->rate = BS2B_DEFAULT_SRATE;
+ mp_msg(MSGT_AFILTER, MSGL_WARN,
+ "[bs2b] Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
+ "[bs2b] Trying to resample to %d Hz.\n",
+ af->data->rate, BS2B_MINSRATE, BS2B_MAXSRATE, BS2B_DEFAULT_SRATE);
+ }
+ bs2b_set_srate(s->filter, (long)af->data->rate);
+ mp_msg(MSGT_AFILTER, MSGL_V, "[bs2b] using format %s\n",
+ af_fmt2str(af->data->format,buf,256));
+
+ return af_test_output(af,(struct mp_audio*)arg);
+ }
+ case AF_CONTROL_COMMAND_LINE: {
+ const opt_t subopts[] = {
+ {"fcut", OPT_ARG_INT, &s->fcut, test_fcut},
+ {"feed", OPT_ARG_INT, &s->feed, test_feed},
+ {"profile", OPT_ARG_MSTRZ, &s->profile, NULL},
+ {NULL}
+ };
+ if (subopt_parse(arg, subopts) != 0) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[bs2b] Invalid option specified.\n");
+ free(s->profile);
+ return AF_ERROR;
+ }
+ // parse profile if specified
+ if (s->profile) {
+ if (!strcmp(s->profile, "default"))
+ bs2b_set_level(s->filter, BS2B_DEFAULT_CLEVEL);
+ else if (!strcmp(s->profile, "cmoy"))
+ bs2b_set_level(s->filter, BS2B_CMOY_CLEVEL);
+ else if (!strcmp(s->profile, "jmeier"))
+ bs2b_set_level(s->filter, BS2B_JMEIER_CLEVEL);
+ else {
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[bs2b] Invalid profile specified: %s.\n"
+ "[bs2b] Available profiles are: default, cmoy, jmeier.\n",
+ s->profile);
+ free(s->profile);
+ return AF_ERROR;
+ }
+ }
+ // set fcut and feed only if specified, otherwise defaults will be used
+ if (s->fcut)
+ bs2b_set_level_fcut(s->filter, s->fcut);
+ if (s->feed)
+ bs2b_set_level_feed(s->filter, s->feed);
+
+ mp_msg(MSGT_AFILTER, MSGL_V,
+ "[bs2b] using cut frequency %d, LF feed level %d\n",
+ bs2b_get_level_fcut(s->filter), bs2b_get_level_feed(s->filter));
+ free(s->profile);
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+/// Deallocate memory and close library
+static void uninit(struct af_instance *af)
+{
+ struct af_bs2b *s = af->setup;
+ free(af->data);
+ if (s && s->filter)
+ bs2b_close(s->filter);
+ free(s);
+}
+
+/// Allocate memory, set function pointers and init library
+static int af_open(struct af_instance *af)
+{
+ struct af_bs2b *s;
+ af->control = control;
+ af->uninit = uninit;
+ af->mul = 1;
+ if (!(af->data = calloc(1, sizeof(struct mp_audio))))
+ return AF_ERROR;
+ if (!(af->setup = s = calloc(1, sizeof(struct af_bs2b)))) {
+ free(af->data);
+ return AF_ERROR;
+ }
+
+ // NULL means failed initialization
+ if (!(s->filter = bs2b_open())) {
+ free(af->data);
+ free(af->setup);
+ return AF_ERROR;
+ }
+ // Set zero defaults indicating no option was specified.
+ s->profile = NULL;
+ s->fcut = 0;
+ s->feed = 0;
+ return AF_OK;
+}
+
+/// Description of this filter
+struct af_info af_info_bs2b = {
+ "Bauer stereophonic-to-binaural audio filter",
+ "bs2b",
+ "Andrew Savchenko",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_center.c b/audio/filter/af_center.c
new file mode 100644
index 0000000000..aa9aae8514
--- /dev/null
+++ b/audio/filter/af_center.c
@@ -0,0 +1,129 @@
+/*
+ * This filter adds a center channel to the audio stream by
+ * averaging the left and right channel.
+ * There are two runtime controls one for setting which channel
+ * to insert the center-audio into called AF_CONTROL_SUB_CH.
+ *
+ * FIXME: implement a high-pass filter for better results.
+ *
+ * copyright (c) 2005 Alex Beregszaszi
+ *
+ * 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 <string.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_center_s
+{
+ int ch; // Channel number which to insert the filtered data
+}af_center_t;
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_center_t* s = af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch);
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+
+ return af_test_output(af,(struct mp_audio*)arg);
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ int ch=1;
+ sscanf(arg,"%i", &ch);
+ return control(af,AF_CONTROL_CENTER_CH | AF_CONTROL_SET, &ch);
+ }
+ case AF_CONTROL_CENTER_CH | AF_CONTROL_SET: // Requires reinit
+ // Sanity check
+ if((*(int*)arg >= AF_NCH) || (*(int*)arg < 0)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Center channel number must be between "
+ " 0 and %i current value is %i\n", AF_NCH-1, *(int*)arg);
+ return AF_ERROR;
+ }
+ s->ch = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_CENTER_CH | AF_CONTROL_GET:
+ *(int*)arg = s->ch;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ af_center_t* s = af->setup; // Setup for this instance
+ float* a = c->audio; // Audio data
+ int len = c->len/4; // Number of samples in current audio block
+ int nch = c->nch; // Number of channels
+ int ch = s->ch; // Channel in which to insert the center audio
+ register int i;
+
+ // Run filter
+ for(i=0;i<len;i+=nch){
+ // Average left and right
+ a[i+ch] = (a[i]/2) + (a[i+1]/2);
+ }
+
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af_center_t* s;
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=s=calloc(1,sizeof(af_center_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ // Set default values
+ s->ch = 1; // Channel nr 2
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_center = {
+ "Audio filter for adding a center channel",
+ "center",
+ "Alex Beregszaszi",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_channels.c b/audio/filter/af_channels.c
new file mode 100644
index 0000000000..8f676d8cfd
--- /dev/null
+++ b/audio/filter/af_channels.c
@@ -0,0 +1,306 @@
+/*
+ * Audio filter that adds and removes channels, according to the
+ * command line parameter channels. It is stupid and can only add
+ * silence or copy channels, not mix or filter.
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+
+#include "af.h"
+
+#define FR 0
+#define TO 1
+
+typedef struct af_channels_s{
+ int route[AF_NCH][2];
+ int nr;
+ int router;
+}af_channels_t;
+
+// Local function for copying data
+static void copy(void* in, void* out, int ins, int inos,int outs, int outos, int len, int bps)
+{
+ switch(bps){
+ case 1:{
+ int8_t* tin = (int8_t*)in;
+ int8_t* tout = (int8_t*)out;
+ tin += inos;
+ tout += outos;
+ len = len/ins;
+ while(len--){
+ *tout=*tin;
+ tin +=ins;
+ tout+=outs;
+ }
+ break;
+ }
+ case 2:{
+ int16_t* tin = (int16_t*)in;
+ int16_t* tout = (int16_t*)out;
+ tin += inos;
+ tout += outos;
+ len = len/(2*ins);
+ while(len--){
+ *tout=*tin;
+ tin +=ins;
+ tout+=outs;
+ }
+ break;
+ }
+ case 3:{
+ int8_t* tin = (int8_t*)in;
+ int8_t* tout = (int8_t*)out;
+ tin += 3 * inos;
+ tout += 3 * outos;
+ len = len / ( 3 * ins);
+ while (len--) {
+ tout[0] = tin[0];
+ tout[1] = tin[1];
+ tout[2] = tin[2];
+ tin += 3 * ins;
+ tout += 3 * outs;
+ }
+ break;
+ }
+ case 4:{
+ int32_t* tin = (int32_t*)in;
+ int32_t* tout = (int32_t*)out;
+ tin += inos;
+ tout += outos;
+ len = len/(4*ins);
+ while(len--){
+ *tout=*tin;
+ tin +=ins;
+ tout+=outs;
+ }
+ break;
+ }
+ case 8:{
+ int64_t* tin = (int64_t*)in;
+ int64_t* tout = (int64_t*)out;
+ tin += inos;
+ tout += outos;
+ len = len/(8*ins);
+ while(len--){
+ *tout=*tin;
+ tin +=ins;
+ tout+=outs;
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Unsupported number of bytes/sample: %i"
+ " please report this error on the MPlayer mailing list. \n",bps);
+ }
+}
+
+// Make sure the routes are sane
+static int check_routes(af_channels_t* s, int nin, int nout)
+{
+ int i;
+ if((s->nr < 1) || (s->nr > AF_NCH)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
+ return AF_ERROR;
+ }
+
+ for(i=0;i<s->nr;i++){
+ if((s->route[i][FR] >= nin) || (s->route[i][TO] >= nout)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Invalid routing in pair nr. %i.\n", i);
+ return AF_ERROR;
+ }
+ }
+ return AF_OK;
+}
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_channels_t* s = af->setup;
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+
+ // Set default channel assignment
+ if(!s->router){
+ int i;
+ // Make sure this filter isn't redundant
+ if(af->data->nch == ((struct mp_audio*)arg)->nch)
+ return AF_DETACH;
+
+ // If mono: fake stereo
+ if(((struct mp_audio*)arg)->nch == 1){
+ s->nr = min(af->data->nch,2);
+ for(i=0;i<s->nr;i++){
+ s->route[i][FR] = 0;
+ s->route[i][TO] = i;
+ }
+ }
+ else{
+ s->nr = min(af->data->nch, ((struct mp_audio*)arg)->nch);
+ for(i=0;i<s->nr;i++){
+ s->route[i][FR] = i;
+ s->route[i][TO] = i;
+ }
+ }
+ }
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->format = ((struct mp_audio*)arg)->format;
+ af->data->bps = ((struct mp_audio*)arg)->bps;
+ af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
+ return check_routes(s,((struct mp_audio*)arg)->nch,af->data->nch);
+ case AF_CONTROL_COMMAND_LINE:{
+ int nch = 0;
+ int n = 0;
+ // Check number of channels and number of routing pairs
+ sscanf(arg, "%i:%i%n", &nch, &s->nr, &n);
+
+ // If router scan commandline for routing pairs
+ if(s->nr){
+ char* cp = &((char*)arg)[n];
+ int ch = 0;
+ // Sanity check
+ if((s->nr < 1) || (s->nr > AF_NCH)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,s->nr);
+ }
+ s->router = 1;
+ // Scan for pairs on commandline
+ while((*cp == ':') && (ch < s->nr)){
+ sscanf(cp, ":%i:%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n);
+ mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Routing from channel %i to"
+ " channel %i\n",s->route[ch][FR],s->route[ch][TO]);
+ cp = &cp[n];
+ ch++;
+ }
+ }
+
+ if(AF_OK != af->control(af,AF_CONTROL_CHANNELS | AF_CONTROL_SET ,&nch))
+ return AF_ERROR;
+ return AF_OK;
+ }
+ case AF_CONTROL_CHANNELS | AF_CONTROL_SET:
+ // Reinit must be called after this function has been called
+
+ // Sanity check
+ if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of output channels must be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
+ return AF_ERROR;
+ }
+
+ af->data->nch=((int*)arg)[0];
+ if(!s->router)
+ mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Changing number of channels"
+ " to %i\n",af->data->nch);
+ return AF_OK;
+ case AF_CONTROL_CHANNELS | AF_CONTROL_GET:
+ *(int*)arg = af->data->nch;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_SET:{
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int* route = ((af_control_ext_t*)arg)->arg;
+ s->route[ch][FR] = route[FR];
+ s->route[ch][TO] = route[TO];
+ return AF_OK;
+ }
+ case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_GET:{
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int* route = ((af_control_ext_t*)arg)->arg;
+ route[FR] = s->route[ch][FR];
+ route[TO] = s->route[ch][TO];
+ return AF_OK;
+ }
+ case AF_CONTROL_CHANNELS_NR | AF_CONTROL_SET:
+ s->nr = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_NR | AF_CONTROL_GET:
+ *(int*)arg = s->nr;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_SET:
+ s->router = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_GET:
+ *(int*)arg = s->router;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->setup);
+ if (af->data)
+ free(af->data->audio);
+ free(af->data);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ struct mp_audio* l = af->data; // Local data
+ af_channels_t* s = af->setup;
+ int i;
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ // Reset unused channels
+ memset(l->audio,0,c->len / c->nch * l->nch);
+
+ if(AF_OK == check_routes(s,c->nch,l->nch))
+ for(i=0;i<s->nr;i++)
+ copy(c->audio,l->audio,c->nch,s->route[i][FR],
+ l->nch,s->route[i][TO],c->len,c->bps);
+
+ // Set output data
+ c->audio = l->audio;
+ c->len = c->len / c->nch * l->nch;
+ c->nch = l->nch;
+
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_channels_t));
+ if((af->data == NULL) || (af->setup == NULL))
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_channels = {
+ "Insert or remove channels",
+ "channels",
+ "Anders",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_delay.c b/audio/filter/af_delay.c
new file mode 100644
index 0000000000..ce8d71980b
--- /dev/null
+++ b/audio/filter/af_delay.c
@@ -0,0 +1,200 @@
+/*
+ * This audio filter delays the output signal for the different
+ * channels and can be used for simple position panning.
+ * An extension for this filter would be a reverb.
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+
+#include "af.h"
+
+#define L 65536
+
+#define UPDATEQI(qi) qi=(qi+1)&(L-1)
+
+// Data for specific instances of this filter
+typedef struct af_delay_s
+{
+ void* q[AF_NCH]; // Circular queues used for delaying audio signal
+ int wi[AF_NCH]; // Write index
+ int ri; // Read index
+ float d[AF_NCH]; // Delay [ms]
+}af_delay_t;
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_delay_t* s = af->setup;
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ int i;
+
+ // Free prevous delay queues
+ for(i=0;i<af->data->nch;i++)
+ free(s->q[i]);
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+ af->data->format = ((struct mp_audio*)arg)->format;
+ af->data->bps = ((struct mp_audio*)arg)->bps;
+
+ // Allocate new delay queues
+ for(i=0;i<af->data->nch;i++){
+ s->q[i] = calloc(L,af->data->bps);
+ if(NULL == s->q[i])
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n");
+ }
+
+ return control(af,AF_CONTROL_DELAY_LEN | AF_CONTROL_SET,s->d);
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ int n = 1;
+ int i = 0;
+ char* cl = arg;
+ while(n && i < AF_NCH ){
+ sscanf(cl,"%f:%n",&s->d[i],&n);
+ if(n==0 || cl[n-1] == '\0')
+ break;
+ cl=&cl[n];
+ i++;
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_DELAY_LEN | AF_CONTROL_SET:{
+ int i;
+ if(AF_OK != af_from_ms(AF_NCH, arg, s->wi, af->data->rate, 0.0, 1000.0))
+ return AF_ERROR;
+ s->ri = 0;
+ for(i=0;i<AF_NCH;i++){
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "[delay] Channel %i delayed by %0.3fms\n",
+ i,clamp(s->d[i],0.0,1000.0));
+ mp_msg(MSGT_AFILTER, MSGL_DBG3, "[delay] Channel %i delayed by %i samples\n",
+ i,s->wi[i]);
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_DELAY_LEN | AF_CONTROL_GET:{
+ int i;
+ for(i=0;i<AF_NCH;i++){
+ if(s->ri > s->wi[i])
+ s->wi[i] = L - (s->ri - s->wi[i]);
+ else
+ s->wi[i] = s->wi[i] - s->ri;
+ }
+ return af_to_ms(AF_NCH, s->wi, arg, af->data->rate);
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ int i;
+
+ free(af->data);
+ for(i=0;i<AF_NCH;i++)
+ free(((af_delay_t*)(af->setup))->q[i]);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ af_delay_t* s = af->setup; // Setup for this instance
+ int nch = c->nch; // Number of channels
+ int len = c->len/c->bps; // Number of sample in data chunk
+ int ri = 0;
+ int ch,i;
+ for(ch=0;ch<nch;ch++){
+ switch(c->bps){
+ case 1:{
+ int8_t* a = c->audio;
+ int8_t* q = s->q[ch];
+ int wi = s->wi[ch];
+ ri = s->ri;
+ for(i=ch;i<len;i+=nch){
+ q[wi] = a[i];
+ a[i] = q[ri];
+ UPDATEQI(wi);
+ UPDATEQI(ri);
+ }
+ s->wi[ch] = wi;
+ break;
+ }
+ case 2:{
+ int16_t* a = c->audio;
+ int16_t* q = s->q[ch];
+ int wi = s->wi[ch];
+ ri = s->ri;
+ for(i=ch;i<len;i+=nch){
+ q[wi] = a[i];
+ a[i] = q[ri];
+ UPDATEQI(wi);
+ UPDATEQI(ri);
+ }
+ s->wi[ch] = wi;
+ break;
+ }
+ case 4:{
+ int32_t* a = c->audio;
+ int32_t* q = s->q[ch];
+ int wi = s->wi[ch];
+ ri = s->ri;
+ for(i=ch;i<len;i+=nch){
+ q[wi] = a[i];
+ a[i] = q[ri];
+ UPDATEQI(wi);
+ UPDATEQI(ri);
+ }
+ s->wi[ch] = wi;
+ break;
+ }
+ }
+ }
+ s->ri = ri;
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_delay_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_delay = {
+ "Delay audio filter",
+ "delay",
+ "Anders",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_dummy.c b/audio/filter/af_dummy.c
new file mode 100644
index 0000000000..29a5b3d4b8
--- /dev/null
+++ b/audio/filter/af_dummy.c
@@ -0,0 +1,76 @@
+/*
+ * The name speaks for itself. This filter is a dummy and will
+ * not blow up regardless of what you do with it.
+ *
+ * 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 <string.h>
+
+#include "af.h"
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ memcpy(af->data,(struct mp_audio*)arg,sizeof(struct mp_audio));
+ mp_msg(MSGT_AFILTER, MSGL_V, "[dummy] Was reinitialized: %iHz/%ich/%s\n",
+ af->data->rate,af->data->nch,af_fmt2str_short(af->data->format));
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ // Do something necessary to get rid of annoying warning during compile
+ if(!af)
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "EEEK: Argument af == NULL in af_dummy.c play().");
+ return data;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=malloc(sizeof(struct mp_audio));
+ if(af->data == NULL)
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_dummy = {
+ "dummy",
+ "dummy",
+ "Anders",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_equalizer.c b/audio/filter/af_equalizer.c
new file mode 100644
index 0000000000..c488ffaeaf
--- /dev/null
+++ b/audio/filter/af_equalizer.c
@@ -0,0 +1,248 @@
+/*
+ * Equalizer filter, implementation of a 10 band time domain graphic
+ * equalizer using IIR filters. The IIR filters are implemented using a
+ * Direct Form II approach, but has been modified (b1 == 0 always) to
+ * save computation.
+ *
+ * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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 <inttypes.h>
+#include <math.h>
+
+#include "af.h"
+
+#define L 2 // Storage for filter taps
+#define KM 10 // Max number of bands
+
+#define Q 1.2247449 /* Q value for band-pass filters 1.2247=(3/2)^(1/2)
+ gives 4dB suppression @ Fc*2 and Fc/2 */
+
+/* Center frequencies for band-pass filters
+ The different frequency bands are:
+ nr. center frequency
+ 0 31.25 Hz
+ 1 62.50 Hz
+ 2 125.0 Hz
+ 3 250.0 Hz
+ 4 500.0 Hz
+ 5 1.000 kHz
+ 6 2.000 kHz
+ 7 4.000 kHz
+ 8 8.000 kHz
+ 9 16.00 kHz
+*/
+#define CF {31.25,62.5,125,250,500,1000,2000,4000,8000,16000}
+
+// Maximum and minimum gain for the bands
+#define G_MAX +12.0
+#define G_MIN -12.0
+
+// Data for specific instances of this filter
+typedef struct af_equalizer_s
+{
+ float a[KM][L]; // A weights
+ float b[KM][L]; // B weights
+ float wq[AF_NCH][KM][L]; // Circular buffer for W data
+ float g[AF_NCH][KM]; // Gain factor for each channel and band
+ int K; // Number of used eq bands
+ int channels; // Number of channels
+ float gain_factor; // applied at output to avoid clipping
+} af_equalizer_t;
+
+// 2nd order Band-pass Filter design
+static void bp2(float* a, float* b, float fc, float q){
+ double th= 2.0 * M_PI * fc;
+ double C = (1.0 - tan(th*q/2.0))/(1.0 + tan(th*q/2.0));
+
+ a[0] = (1.0 + C) * cos(th);
+ a[1] = -1 * C;
+
+ b[0] = (1.0 - C)/2.0;
+ b[1] = -1.0050;
+}
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_equalizer_t* s = (af_equalizer_t*)af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ int k =0, i =0;
+ float F[KM] = CF;
+
+ s->gain_factor=0.0;
+
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+
+ // Calculate number of active filters
+ s->K=KM;
+ while(F[s->K-1] > (float)af->data->rate/2.2)
+ s->K--;
+
+ if(s->K != KM)
+ mp_msg(MSGT_AFILTER, MSGL_INFO, "[equalizer] Limiting the number of filters to"
+ " %i due to low sample rate.\n",s->K);
+
+ // Generate filter taps
+ for(k=0;k<s->K;k++)
+ bp2(s->a[k],s->b[k],F[k]/((float)af->data->rate),Q);
+
+ // Calculate how much this plugin adds to the overall time delay
+ af->delay = 2 * af->data->nch * af->data->bps;
+
+ // Calculate gain factor to prevent clipping at output
+ for(k=0;k<AF_NCH;k++)
+ {
+ for(i=0;i<KM;i++)
+ {
+ if(s->gain_factor < s->g[k][i]) s->gain_factor=s->g[k][i];
+ }
+ }
+
+ s->gain_factor=log10(s->gain_factor + 1.0) * 20.0;
+
+ if(s->gain_factor > 0.0)
+ {
+ s->gain_factor=0.1+(s->gain_factor/12.0);
+ }else{
+ s->gain_factor=1;
+ }
+
+ return af_test_output(af,arg);
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ float g[10]={0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+ int i,j;
+ sscanf((char*)arg,"%f:%f:%f:%f:%f:%f:%f:%f:%f:%f", &g[0], &g[1],
+ &g[2], &g[3], &g[4], &g[5], &g[6], &g[7], &g[8] ,&g[9]);
+ for(i=0;i<AF_NCH;i++){
+ for(j=0;j<KM;j++){
+ ((af_equalizer_t*)af->setup)->g[i][j] =
+ pow(10.0,clamp(g[j],G_MIN,G_MAX)/20.0)-1.0;
+ }
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_SET:{
+ float* gain = ((af_control_ext_t*)arg)->arg;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int k;
+ if(ch >= AF_NCH || ch < 0)
+ return AF_ERROR;
+
+ for(k = 0 ; k<KM ; k++)
+ s->g[ch][k] = pow(10.0,clamp(gain[k],G_MIN,G_MAX)/20.0)-1.0;
+
+ return AF_OK;
+ }
+ case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_GET:{
+ float* gain = ((af_control_ext_t*)arg)->arg;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ int k;
+ if(ch >= AF_NCH || ch < 0)
+ return AF_ERROR;
+
+ for(k = 0 ; k<KM ; k++)
+ gain[k] = log10(s->g[ch][k]+1.0) * 20.0;
+
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ af_equalizer_t* s = (af_equalizer_t*)af->setup; // Setup
+ uint32_t ci = af->data->nch; // Index for channels
+ uint32_t nch = af->data->nch; // Number of channels
+
+ while(ci--){
+ float* g = s->g[ci]; // Gain factor
+ float* in = ((float*)c->audio)+ci;
+ float* out = ((float*)c->audio)+ci;
+ float* end = in + c->len/4; // Block loop end
+
+ while(in < end){
+ register int k = 0; // Frequency band index
+ register float yt = *in; // Current input sample
+ in+=nch;
+
+ // Run the filters
+ for(;k<s->K;k++){
+ // Pointer to circular buffer wq
+ register float* wq = s->wq[ci][k];
+ // Calculate output from AR part of current filter
+ register float w=yt*s->b[k][0] + wq[0]*s->a[k][0] + wq[1]*s->a[k][1];
+ // Calculate output form MA part of current filter
+ yt+=(w + wq[1]*s->b[k][1])*g[k];
+ // Update circular buffer
+ wq[1] = wq[0];
+ wq[0] = w;
+ }
+ // Calculate output
+ *out=yt*s->gain_factor;
+ out+=nch;
+ }
+ }
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_equalizer_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_equalizer = {
+ "Equalizer audio filter",
+ "equalizer",
+ "Anders",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_export.c b/audio/filter/af_export.c
new file mode 100644
index 0000000000..441ec31ac3
--- /dev/null
+++ b/audio/filter/af_export.c
@@ -0,0 +1,273 @@
+/*
+ * This audio filter exports the incoming signal to other processes
+ * using memory mapping. The memory mapped area contains a header:
+ * int nch,
+ * int size,
+ * unsigned long long counter (updated every time the contents of
+ * the area changes),
+ * the rest is payload (non-interleaved).
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "af.h"
+#include "path.h"
+
+#define DEF_SZ 512 // default buffer size (in samples)
+#define SHARED_FILE "mpv-af_export" /* default file name
+ (relative to ~/.mpv/ */
+
+#define SIZE_HEADER (2 * sizeof(int) + sizeof(unsigned long long))
+
+// Data for specific instances of this filter
+typedef struct af_export_s
+{
+ unsigned long long count; // Used for sync
+ void* buf[AF_NCH]; // Buffers for storing the data before it is exported
+ int sz; // Size of buffer in samples
+ int wi; // Write index
+ int fd; // File descriptor to shared memory area
+ char* filename; // File to export data
+ uint8_t *mmap_area; // MMap shared area
+} af_export_t;
+
+
+/* Initialization and runtime control
+ af audio filter instance
+ cmd control command
+ arg argument
+*/
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_export_t* s = af->setup;
+ switch (cmd){
+ case AF_CONTROL_REINIT:{
+ int i=0;
+ int mapsize;
+
+ // Free previous buffers
+ if (s->buf)
+ free(s->buf[0]);
+
+ // unmap previous area
+ if(s->mmap_area)
+ munmap(s->mmap_area, SIZE_HEADER + (af->data->bps*s->sz*af->data->nch));
+ // close previous file descriptor
+ if(s->fd)
+ close(s->fd);
+
+ // Accept only int16_t as input format (which sucks)
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+
+ // If buffer length isn't set, set it to the default value
+ if(s->sz == 0)
+ s->sz = DEF_SZ;
+
+ // Allocate new buffers (as one continuous block)
+ s->buf[0] = calloc(s->sz*af->data->nch, af->data->bps);
+ if(NULL == s->buf[0])
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Out of memory\n");
+ for(i = 1; i < af->data->nch; i++)
+ s->buf[i] = (uint8_t *)s->buf[0] + i*s->sz*af->data->bps;
+
+ // Init memory mapping
+ s->fd = open(s->filename, O_RDWR | O_CREAT | O_TRUNC, 0640);
+ mp_msg(MSGT_AFILTER, MSGL_INFO, "[export] Exporting to file: %s\n", s->filename);
+ if(s->fd < 0)
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Could not open/create file: %s\n",
+ s->filename);
+
+ // header + buffer
+ mapsize = (SIZE_HEADER + (af->data->bps * s->sz * af->data->nch));
+
+ // grow file to needed size
+ for(i = 0; i < mapsize; i++){
+ char null = 0;
+ write(s->fd, (void*) &null, 1);
+ }
+
+ // mmap size
+ s->mmap_area = mmap(0, mapsize, PROT_READ|PROT_WRITE,MAP_SHARED, s->fd, 0);
+ if(s->mmap_area == NULL)
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Could not mmap file %s\n", s->filename);
+ mp_msg(MSGT_AFILTER, MSGL_INFO, "[export] Memory mapped to file: %s (%p)\n",
+ s->filename, s->mmap_area);
+
+ // Initialize header
+ *((int*)s->mmap_area) = af->data->nch;
+ *((int*)s->mmap_area + 1) = s->sz * af->data->bps * af->data->nch;
+ msync(s->mmap_area, mapsize, MS_ASYNC);
+
+ // Use test_output to return FALSE if necessary
+ return af_test_output(af, (struct mp_audio*)arg);
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ int i=0;
+ char *str = arg;
+
+ if (!str){
+ free(s->filename);
+
+ s->filename = get_path(SHARED_FILE);
+ return AF_OK;
+ }
+
+ while((str[i]) && (str[i] != ':'))
+ i++;
+
+ free(s->filename);
+
+ s->filename = calloc(i + 1, 1);
+ memcpy(s->filename, str, i);
+ s->filename[i] = 0;
+
+ sscanf(str + i + 1, "%d", &(s->sz));
+
+ return af->control(af, AF_CONTROL_EXPORT_SZ | AF_CONTROL_SET, &s->sz);
+ }
+ case AF_CONTROL_EXPORT_SZ | AF_CONTROL_SET:
+ s->sz = * (int *) arg;
+ if((s->sz <= 0) || (s->sz > 2048))
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[export] Buffer size must be between"
+ " 1 and 2048\n" );
+
+ return AF_OK;
+ case AF_CONTROL_EXPORT_SZ | AF_CONTROL_GET:
+ *(int*) arg = s->sz;
+ return AF_OK;
+
+ }
+ return AF_UNKNOWN;
+}
+
+/* Free allocated memory and clean up other stuff too.
+ af audio filter instance
+*/
+static void uninit( struct af_instance* af )
+{
+ free(af->data);
+ af->data = NULL;
+
+ if(af->setup){
+ af_export_t* s = af->setup;
+ if (s->buf)
+ free(s->buf[0]);
+
+ // Free mmaped area
+ if(s->mmap_area)
+ munmap(s->mmap_area, sizeof(af_export_t));
+
+ if(s->fd > -1)
+ close(s->fd);
+
+ free(s->filename);
+
+ free(af->setup);
+ af->setup = NULL;
+ }
+}
+
+/* Filter data through filter
+ af audio filter instance
+ data audio data
+*/
+static struct mp_audio* play( struct af_instance* af, struct mp_audio* data )
+{
+ struct mp_audio* c = data; // Current working data
+ af_export_t* s = af->setup; // Setup for this instance
+ int16_t* a = c->audio; // Incomming sound
+ int nch = c->nch; // Number of channels
+ int len = c->len/c->bps; // Number of sample in data chunk
+ int sz = s->sz; // buffer size (in samples)
+ int flag = 0; // Set to 1 if buffer is filled
+
+ int ch, i;
+
+ // Fill all buffers
+ for(ch = 0; ch < nch; ch++){
+ int wi = s->wi; // Reset write index
+ int16_t* b = s->buf[ch]; // Current buffer
+
+ // Copy data to export buffers
+ for(i = ch; i < len; i += nch){
+ b[wi++] = a[i];
+ if(wi >= sz){ // Don't write outside the end of the buffer
+ flag = 1;
+ break;
+ }
+ }
+ s->wi = wi % s->sz;
+ }
+
+ // Export buffer to mmaped area
+ if(flag){
+ // update buffer in mapped area
+ memcpy(s->mmap_area + SIZE_HEADER, s->buf[0], sz * c->bps * nch);
+ s->count++; // increment counter (to sync)
+ memcpy(s->mmap_area + SIZE_HEADER - sizeof(s->count),
+ &(s->count), sizeof(s->count));
+ }
+
+ // We don't modify data, just export it
+ return data;
+}
+
+/* Allocate memory and set function pointers
+ af audio filter instance
+ returns AF_OK or AF_ERROR
+*/
+static int af_open( struct af_instance* af )
+{
+ af->control = control;
+ af->uninit = uninit;
+ af->play = play;
+ af->mul=1;
+ af->data = calloc(1, sizeof(struct mp_audio));
+ af->setup = calloc(1, sizeof(af_export_t));
+ if((af->data == NULL) || (af->setup == NULL))
+ return AF_ERROR;
+
+ ((af_export_t *)af->setup)->filename = get_path(SHARED_FILE);
+
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_export = {
+ "Sound export filter",
+ "export",
+ "Anders; Gustavo Sverzut Barbieri <gustavo.barbieri@ic.unicamp.br>",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_extrastereo.c b/audio/filter/af_extrastereo.c
new file mode 100644
index 0000000000..0f7fe36861
--- /dev/null
+++ b/audio/filter/af_extrastereo.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard
+ *
+ * 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 <string.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_extrastereo_s
+{
+ float mul;
+}af_extrastereo_t;
+
+static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data);
+static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data);
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_extrastereo_t* s = (af_extrastereo_t*)af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = 2;
+ if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
+ {
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ af->play = play_float;
+ }// else
+ {
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ af->play = play_s16;
+ }
+
+ return af_test_output(af,(struct mp_audio*)arg);
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ float f;
+ sscanf((char*)arg,"%f", &f);
+ s->mul = f;
+ return AF_OK;
+ }
+ case AF_CONTROL_ES_MUL | AF_CONTROL_SET:
+ s->mul = *(float*)arg;
+ return AF_OK;
+ case AF_CONTROL_ES_MUL | AF_CONTROL_GET:
+ *(float*)arg = s->mul;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
+{
+ af_extrastereo_t *s = af->setup;
+ register int i = 0;
+ int16_t *a = (int16_t*)data->audio; // Audio data
+ int len = data->len/2; // Number of samples
+ int avg, l, r;
+
+ for (i = 0; i < len; i+=2)
+ {
+ avg = (a[i] + a[i + 1]) / 2;
+
+ l = avg + (int)(s->mul * (a[i] - avg));
+ r = avg + (int)(s->mul * (a[i + 1] - avg));
+
+ a[i] = clamp(l, SHRT_MIN, SHRT_MAX);
+ a[i + 1] = clamp(r, SHRT_MIN, SHRT_MAX);
+ }
+
+ return data;
+}
+
+static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data)
+{
+ af_extrastereo_t *s = af->setup;
+ register int i = 0;
+ float *a = (float*)data->audio; // Audio data
+ int len = data->len/4; // Number of samples
+ float avg, l, r;
+
+ for (i = 0; i < len; i+=2)
+ {
+ avg = (a[i] + a[i + 1]) / 2;
+
+ l = avg + (s->mul * (a[i] - avg));
+ r = avg + (s->mul * (a[i + 1] - avg));
+
+ a[i] = af_softclip(l);
+ a[i + 1] = af_softclip(r);
+ }
+
+ return data;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play_s16;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_extrastereo_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+
+ ((af_extrastereo_t*)af->setup)->mul = 2.5;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_extrastereo = {
+ "Increase difference between audio channels",
+ "extrastereo",
+ "Alex Beregszaszi & Pierre Lombard",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c
new file mode 100644
index 0000000000..4ac9caaa85
--- /dev/null
+++ b/audio/filter/af_format.c
@@ -0,0 +1,519 @@
+/*
+ * This audio filter changes the format of a data block. Valid
+ * formats are: AFMT_U8, AFMT_S8, AFMT_S16_LE, AFMT_S16_BE
+ * AFMT_U16_LE, AFMT_U16_BE, AFMT_S32_LE and AFMT_S32_BE.
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "af.h"
+#include "mpbswap.h"
+
+/* Functions used by play to convert the input audio to the correct
+ format */
+
+/* The below includes retrieves functions for converting to and from
+ ulaw and alaw */
+#include "af_format_ulaw.h"
+#include "af_format_alaw.h"
+
+// Switch endianness
+static void endian(void* in, void* out, int len, int bps);
+// From signed to unsigned and the other way
+static void si2us(void* data, int len, int bps);
+// Change the number of bits per sample
+static void change_bps(void* in, void* out, int len, int inbps, int outbps);
+// From float to int signed
+static void float2int(float* in, void* out, int len, int bps);
+// From signed int to float
+static void int2float(void* in, float* out, int len, int bps);
+
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data);
+static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data);
+static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data);
+static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data);
+
+// Helper functions to check sanity for input arguments
+
+// Sanity check for bytes per sample
+static int check_bps(int bps)
+{
+ if(bps != 4 && bps != 3 && bps != 2 && bps != 1){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] The number of bytes per sample"
+ " must be 1, 2, 3 or 4. Current value is %i \n",bps);
+ return AF_ERROR;
+ }
+ return AF_OK;
+}
+
+// Check for unsupported formats
+static int check_format(int format)
+{
+ char buf[256];
+ switch(format & AF_FORMAT_SPECIAL_MASK){
+ case(AF_FORMAT_IMA_ADPCM):
+ case(AF_FORMAT_MPEG2):
+ case(AF_FORMAT_AC3):
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] Sample format %s not yet supported \n",
+ af_fmt2str(format,buf,256));
+ return AF_ERROR;
+ }
+ return AF_OK;
+}
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ char buf1[256];
+ char buf2[256];
+ struct mp_audio *data = arg;
+
+ // Make sure this filter isn't redundant
+ if(af->data->format == data->format &&
+ af->data->bps == data->bps)
+ return AF_DETACH;
+
+ // Allow trivial AC3-endianness conversion
+ if (!AF_FORMAT_IS_AC3(af->data->format) || !AF_FORMAT_IS_AC3(data->format))
+ // Check for errors in configuration
+ if((AF_OK != check_bps(data->bps)) ||
+ (AF_OK != check_format(data->format)) ||
+ (AF_OK != check_bps(af->data->bps)) ||
+ (AF_OK != check_format(af->data->format)))
+ return AF_ERROR;
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "[format] Changing sample format from %s to %s\n",
+ af_fmt2str(data->format,buf1,256),
+ af_fmt2str(af->data->format,buf2,256));
+
+ af->data->rate = data->rate;
+ af->data->nch = data->nch;
+ af->mul = (double)af->data->bps / data->bps;
+
+ af->play = play; // set default
+
+ // look whether only endianness differences are there
+ if ((af->data->format & ~AF_FORMAT_END_MASK) ==
+ (data->format & ~AF_FORMAT_END_MASK))
+ {
+ mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated endianness conversion only\n");
+ af->play = play_swapendian;
+ }
+ if ((data->format == AF_FORMAT_FLOAT_NE) &&
+ (af->data->format == AF_FORMAT_S16_NE))
+ {
+ mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n",
+ af_fmt2str(data->format,buf1,256),
+ af_fmt2str(af->data->format,buf2,256));
+ af->play = play_float_s16;
+ }
+ if ((data->format == AF_FORMAT_S16_NE) &&
+ (af->data->format == AF_FORMAT_FLOAT_NE))
+ {
+ mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n",
+ af_fmt2str(data->format,buf1,256),
+ af_fmt2str(af->data->format,buf2,256));
+ af->play = play_s16_float;
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ int format = af_str2fmt_short(bstr0(arg));
+ if (format == -1) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] %s is not a valid format\n", (char *)arg);
+ return AF_ERROR;
+ }
+ if(AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format))
+ return AF_ERROR;
+ return AF_OK;
+ }
+ case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET:{
+ // Check for errors in configuration
+ if(!AF_FORMAT_IS_AC3(*(int*)arg) && AF_OK != check_format(*(int*)arg))
+ return AF_ERROR;
+
+ af->data->format = *(int*)arg;
+ af->data->bps = af_fmt2bits(af->data->format)/8;
+
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ if (af->data)
+ free(af->data->audio);
+ free(af->data);
+ af->setup = 0;
+}
+
+static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* l = af->data; // Local data
+ struct mp_audio* c = data; // Current working data
+ int len = c->len/c->bps; // Length in samples of current audio block
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ endian(c->audio,l->audio,len,c->bps);
+
+ c->audio = l->audio;
+ c->format = l->format;
+
+ return c;
+}
+
+static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* l = af->data; // Local data
+ struct mp_audio* c = data; // Current working data
+ int len = c->len/4; // Length in samples of current audio block
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ float2int(c->audio, l->audio, len, 2);
+
+ c->audio = l->audio;
+ c->len = len*2;
+ c->bps = 2;
+ c->format = l->format;
+
+ return c;
+}
+
+static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* l = af->data; // Local data
+ struct mp_audio* c = data; // Current working data
+ int len = c->len/2; // Length in samples of current audio block
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ int2float(c->audio, l->audio, len, 2);
+
+ c->audio = l->audio;
+ c->len = len*4;
+ c->bps = 4;
+ c->format = l->format;
+
+ return c;
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* l = af->data; // Local data
+ struct mp_audio* c = data; // Current working data
+ int len = c->len/c->bps; // Length in samples of current audio block
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ // Change to cpu native endian format
+ if((c->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE)
+ endian(c->audio,c->audio,len,c->bps);
+
+ // Conversion table
+ if((c->format & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_MU_LAW) {
+ from_ulaw(c->audio, l->audio, len, l->bps, l->format&AF_FORMAT_POINT_MASK);
+ if(AF_FORMAT_A_LAW == (l->format&AF_FORMAT_SPECIAL_MASK))
+ to_ulaw(l->audio, l->audio, len, 1, AF_FORMAT_SI);
+ if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
+ si2us(l->audio,len,l->bps);
+ } else if((c->format & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_A_LAW) {
+ from_alaw(c->audio, l->audio, len, l->bps, l->format&AF_FORMAT_POINT_MASK);
+ if(AF_FORMAT_A_LAW == (l->format&AF_FORMAT_SPECIAL_MASK))
+ to_alaw(l->audio, l->audio, len, 1, AF_FORMAT_SI);
+ if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
+ si2us(l->audio,len,l->bps);
+ } else if((c->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) {
+ switch(l->format&AF_FORMAT_SPECIAL_MASK){
+ case(AF_FORMAT_MU_LAW):
+ to_ulaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK);
+ break;
+ case(AF_FORMAT_A_LAW):
+ to_alaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK);
+ break;
+ default:
+ float2int(c->audio, l->audio, len, l->bps);
+ if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
+ si2us(l->audio,len,l->bps);
+ break;
+ }
+ } else {
+ // Input must be int
+
+ // Change signed/unsigned
+ if((c->format&AF_FORMAT_SIGN_MASK) != (l->format&AF_FORMAT_SIGN_MASK)){
+ si2us(c->audio,len,c->bps);
+ }
+ // Convert to special formats
+ switch(l->format&(AF_FORMAT_SPECIAL_MASK|AF_FORMAT_POINT_MASK)){
+ case(AF_FORMAT_MU_LAW):
+ to_ulaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK);
+ break;
+ case(AF_FORMAT_A_LAW):
+ to_alaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK);
+ break;
+ case(AF_FORMAT_F):
+ int2float(c->audio, l->audio, len, c->bps);
+ break;
+ default:
+ // Change the number of bits
+ if(c->bps != l->bps)
+ change_bps(c->audio,l->audio,len,c->bps,l->bps);
+ else
+ memcpy(l->audio,c->audio,len*c->bps);
+ break;
+ }
+ }
+
+ // Switch from cpu native endian to the correct endianness
+ if((l->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE)
+ endian(l->audio,l->audio,len,l->bps);
+
+ // Set output data
+ c->audio = l->audio;
+ c->len = len*l->bps;
+ c->bps = l->bps;
+ c->format = l->format;
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ if(af->data == NULL)
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_format = {
+ "Sample format conversion",
+ "format",
+ "Anders",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
+
+static inline uint32_t load24bit(void* data, int pos) {
+#if BYTE_ORDER == BIG_ENDIAN
+ return (((uint32_t)((uint8_t*)data)[3*pos])<<24) |
+ (((uint32_t)((uint8_t*)data)[3*pos+1])<<16) |
+ (((uint32_t)((uint8_t*)data)[3*pos+2])<<8);
+#else
+ return (((uint32_t)((uint8_t*)data)[3*pos])<<8) |
+ (((uint32_t)((uint8_t*)data)[3*pos+1])<<16) |
+ (((uint32_t)((uint8_t*)data)[3*pos+2])<<24);
+#endif
+}
+
+static inline void store24bit(void* data, int pos, uint32_t expanded_value) {
+#if BYTE_ORDER == BIG_ENDIAN
+ ((uint8_t*)data)[3*pos]=expanded_value>>24;
+ ((uint8_t*)data)[3*pos+1]=expanded_value>>16;
+ ((uint8_t*)data)[3*pos+2]=expanded_value>>8;
+#else
+ ((uint8_t*)data)[3*pos]=expanded_value>>8;
+ ((uint8_t*)data)[3*pos+1]=expanded_value>>16;
+ ((uint8_t*)data)[3*pos+2]=expanded_value>>24;
+#endif
+}
+
+// Function implementations used by play
+static void endian(void* in, void* out, int len, int bps)
+{
+ register int i;
+ switch(bps){
+ case(2):{
+ for(i=0;i<len;i++){
+ ((uint16_t*)out)[i]=bswap_16(((uint16_t*)in)[i]);
+ }
+ break;
+ }
+ case(3):{
+ register uint8_t s;
+ for(i=0;i<len;i++){
+ s=((uint8_t*)in)[3*i];
+ ((uint8_t*)out)[3*i]=((uint8_t*)in)[3*i+2];
+ if (in != out)
+ ((uint8_t*)out)[3*i+1]=((uint8_t*)in)[3*i+1];
+ ((uint8_t*)out)[3*i+2]=s;
+ }
+ break;
+ }
+ case(4):{
+ for(i=0;i<len;i++){
+ ((uint32_t*)out)[i]=bswap_32(((uint32_t*)in)[i]);
+ }
+ break;
+ }
+ }
+}
+
+static void si2us(void* data, int len, int bps)
+{
+ register long i = -(len * bps);
+ register uint8_t *p = &((uint8_t *)data)[len * bps];
+#if AF_FORMAT_NE == AF_FORMAT_LE
+ p += bps - 1;
+#endif
+ if (len <= 0) return;
+ do {
+ p[i] ^= 0x80;
+ } while (i += bps);
+}
+
+static void change_bps(void* in, void* out, int len, int inbps, int outbps)
+{
+ register int i;
+ switch(inbps){
+ case(1):
+ switch(outbps){
+ case(2):
+ for(i=0;i<len;i++)
+ ((uint16_t*)out)[i]=((uint16_t)((uint8_t*)in)[i])<<8;
+ break;
+ case(3):
+ for(i=0;i<len;i++)
+ store24bit(out, i, ((uint32_t)((uint8_t*)in)[i])<<24);
+ break;
+ case(4):
+ for(i=0;i<len;i++)
+ ((uint32_t*)out)[i]=((uint32_t)((uint8_t*)in)[i])<<24;
+ break;
+ }
+ break;
+ case(2):
+ switch(outbps){
+ case(1):
+ for(i=0;i<len;i++)
+ ((uint8_t*)out)[i]=(uint8_t)((((uint16_t*)in)[i])>>8);
+ break;
+ case(3):
+ for(i=0;i<len;i++)
+ store24bit(out, i, ((uint32_t)((uint16_t*)in)[i])<<16);
+ break;
+ case(4):
+ for(i=0;i<len;i++)
+ ((uint32_t*)out)[i]=((uint32_t)((uint16_t*)in)[i])<<16;
+ break;
+ }
+ break;
+ case(3):
+ switch(outbps){
+ case(1):
+ for(i=0;i<len;i++)
+ ((uint8_t*)out)[i]=(uint8_t)(load24bit(in, i)>>24);
+ break;
+ case(2):
+ for(i=0;i<len;i++)
+ ((uint16_t*)out)[i]=(uint16_t)(load24bit(in, i)>>16);
+ break;
+ case(4):
+ for(i=0;i<len;i++)
+ ((uint32_t*)out)[i]=(uint32_t)load24bit(in, i);
+ break;
+ }
+ break;
+ case(4):
+ switch(outbps){
+ case(1):
+ for(i=0;i<len;i++)
+ ((uint8_t*)out)[i]=(uint8_t)((((uint32_t*)in)[i])>>24);
+ break;
+ case(2):
+ for(i=0;i<len;i++)
+ ((uint16_t*)out)[i]=(uint16_t)((((uint32_t*)in)[i])>>16);
+ break;
+ case(3):
+ for(i=0;i<len;i++)
+ store24bit(out, i, ((uint32_t*)in)[i]);
+ break;
+ }
+ break;
+ }
+}
+
+static void float2int(float* in, void* out, int len, int bps)
+{
+ register int i;
+ switch(bps){
+ case(1):
+ for(i=0;i<len;i++)
+ ((int8_t*)out)[i] = lrintf(127.0 * clamp(in[i], -1.0f, +1.0f));
+ break;
+ case(2):
+ for(i=0;i<len;i++)
+ ((int16_t*)out)[i] = lrintf(32767.0 * clamp(in[i], -1.0f, +1.0f));
+ break;
+ case(3):
+ for(i=0;i<len;i++)
+ store24bit(out, i, lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f)));
+ break;
+ case(4):
+ for(i=0;i<len;i++)
+ ((int32_t*)out)[i] = lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f));
+ break;
+ }
+}
+
+static void int2float(void* in, float* out, int len, int bps)
+{
+ register int i;
+ switch(bps){
+ case(1):
+ for(i=0;i<len;i++)
+ out[i]=(1.0/128.0)*((int8_t*)in)[i];
+ break;
+ case(2):
+ for(i=0;i<len;i++)
+ out[i]=(1.0/32768.0)*((int16_t*)in)[i];
+ break;
+ case(3):
+ for(i=0;i<len;i++)
+ out[i]=(1.0/2147483648.0)*((int32_t)load24bit(in, i));
+ break;
+ case(4):
+ for(i=0;i<len;i++)
+ out[i]=(1.0/2147483648.0)*((int32_t*)in)[i];
+ break;
+ }
+}
diff --git a/audio/filter/af_format_alaw.h b/audio/filter/af_format_alaw.h
new file mode 100644
index 0000000000..d7c00884f7
--- /dev/null
+++ b/audio/filter/af_format_alaw.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au
+ *
+ * This file is based on a part of libsndfile, the work of
+ * Erik de Castro Lopo <erikd@zip.com.au>.
+ *
+ * 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.
+ */
+
+#ifndef MPLAYER_AF_FORMAT_ALAW_H
+#define MPLAYER_AF_FORMAT_ALAW_H
+
+#include <inttypes.h>
+
+#include "af.h"
+
+// Conversion tables (the function are below)
+static short alaw_decode [128] =
+{ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+ -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+ -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+ -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+ -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
+ -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
+ -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
+ -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
+ -344, -328, -376, -360, -280, -264, -312, -296,
+ -472, -456, -504, -488, -408, -392, -440, -424,
+ -88, -72, -120, -104, -24, -8, -56, -40,
+ -216, -200, -248, -232, -152, -136, -184, -168,
+ -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+ -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624, -592,
+ -944, -912, -1008, -976, -816, -784, -880, -848
+} ; /* alaw_decode */
+
+static unsigned char alaw_encode [2049] =
+{ 0xD5, 0xD4, 0xD7, 0xD6, 0xD1, 0xD0, 0xD3, 0xD2, 0xDD, 0xDC, 0xDF, 0xDE,
+ 0xD9, 0xD8, 0xDB, 0xDA, 0xC5, 0xC4, 0xC7, 0xC6, 0xC1, 0xC0, 0xC3, 0xC2,
+ 0xCD, 0xCC, 0xCF, 0xCE, 0xC9, 0xC8, 0xCB, 0xCA, 0xF5, 0xF5, 0xF4, 0xF4,
+ 0xF7, 0xF7, 0xF6, 0xF6, 0xF1, 0xF1, 0xF0, 0xF0, 0xF3, 0xF3, 0xF2, 0xF2,
+ 0xFD, 0xFD, 0xFC, 0xFC, 0xFF, 0xFF, 0xFE, 0xFE, 0xF9, 0xF9, 0xF8, 0xF8,
+ 0xFB, 0xFB, 0xFA, 0xFA, 0xE5, 0xE5, 0xE5, 0xE5, 0xE4, 0xE4, 0xE4, 0xE4,
+ 0xE7, 0xE7, 0xE7, 0xE7, 0xE6, 0xE6, 0xE6, 0xE6, 0xE1, 0xE1, 0xE1, 0xE1,
+ 0xE0, 0xE0, 0xE0, 0xE0, 0xE3, 0xE3, 0xE3, 0xE3, 0xE2, 0xE2, 0xE2, 0xE2,
+ 0xED, 0xED, 0xED, 0xED, 0xEC, 0xEC, 0xEC, 0xEC, 0xEF, 0xEF, 0xEF, 0xEF,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xE9, 0xE9, 0xE9, 0xE9, 0xE8, 0xE8, 0xE8, 0xE8,
+ 0xEB, 0xEB, 0xEB, 0xEB, 0xEA, 0xEA, 0xEA, 0xEA, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0xB5, 0xB5, 0xB5, 0xB5,
+ 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+ 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+ 0xB5, 0xB5, 0xB5, 0xB5, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+ 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+ 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+ 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+ 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+ 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6, 0xB6, 0xB6, 0xB6,
+ 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+ 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+ 0xB6, 0xB6, 0xB6, 0xB6, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+ 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+ 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+ 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+ 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+ 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB3, 0xB3, 0xB3, 0xB3,
+ 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+ 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+ 0xB3, 0xB3, 0xB3, 0xB3, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+ 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+ 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+ 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+ 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+ 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBC, 0xBC, 0xBC, 0xBC,
+ 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+ 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+ 0xBC, 0xBC, 0xBC, 0xBC, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+ 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+ 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xB9, 0xB9, 0xB9, 0xB9,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+ 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+ 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBA, 0xBA, 0xBA, 0xBA,
+ 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+ 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+ 0xBA, 0xBA, 0xBA, 0xBA, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x2A
+} ; /* alaw_encode */
+
+/* Convert from alaw to signd int8 to signed int32 or float */
+static int from_alaw(void* in, void* out, int len, int bps, int format)
+{
+ register int i;
+ // Make sure the input parametrs are OK
+ if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US))
+ return AF_ERROR;
+
+ // Convert to int or to float
+ if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){
+ switch(bps){
+ case(1):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((int8_t*)out)[i] = (-1 * alaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8;
+ else
+ ((int8_t*)out)[i] = (alaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8;
+ }
+ break;
+ case(2):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((int16_t*)out)[i] = -1 * alaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ else
+ ((int16_t*)out)[i] = alaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ }
+ break;
+ case(4):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((int32_t*)out)[i] = (-1 * alaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16;
+ else
+ ((int32_t*)out)[i] = (alaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16;
+ }
+ break;
+ default:
+ return AF_ERROR;
+ }
+ }
+ else{
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((float*)out)[i] = -1.0/32768.0 * (float)alaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ else
+ ((float*)out)[i] = +1.0/32768.0 * (float)alaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ }
+ }
+ return AF_OK;
+}
+
+/* Convert from signed int8 to signed int32 or float to alaw */
+static int to_alaw(void* in, void* out, int len, int bps, int format)
+{
+ register int i;
+ // Make sure the input parametrs are OK
+ if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US))
+ return AF_ERROR;
+
+ // Convert from int or to float
+ if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){
+ switch(bps){
+ case(1):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] >= 0)
+ ((int8_t*)out)[i] = alaw_encode[((int8_t*)in)[i] << 4];
+ else
+ ((int8_t*)out)[i] = 0x7F & alaw_encode[-((int8_t*)in)[i] << 4];
+ }
+ break;
+ case(2):
+ for(i=0;i<len;i++){
+ if(((int16_t*)in)[i] >= 0)
+ ((int8_t*)out)[i] = alaw_encode[((int16_t*)in)[i] / 16];
+ else
+ ((int8_t*)out)[i] = 0x7F & alaw_encode[((int16_t*)in)[i] / -16];
+ }
+ break;
+ case(4):
+ for(i=0;i<len;i++){
+ if(((int32_t*)in)[i] >= 0)
+ ((int8_t*)out)[i] = alaw_encode[((int32_t*)in)[i] >> (16 + 4)];
+ else
+ ((int8_t*)out)[i] = 0x7F & alaw_encode[-((int32_t*)in)[i] >> (16 + 4)];
+ }
+ break;
+ default:
+ return AF_ERROR;
+ }
+ }
+ else{
+ for(i=0;i<len;i++){
+ if(((float*)in)[i] >= 0)
+ ((int8_t*)out)[i] = alaw_encode[(int)(32767.0/16.0 * ((float*)in)[i])];
+ else
+ ((int8_t*)out)[i] = 0x7F & alaw_encode[(int)(-32767.0/16.0 * ((float*)in)[i])];
+ }
+ }
+ return AF_OK;
+}
+#endif /* MPLAYER_AF_FORMAT_ALAW_H */
diff --git a/audio/filter/af_format_ulaw.h b/audio/filter/af_format_ulaw.h
new file mode 100644
index 0000000000..5167a1593a
--- /dev/null
+++ b/audio/filter/af_format_ulaw.h
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au
+ *
+ * This file is based on a part of libsndfile, the work of
+ * Erik de Castro Lopo <erikd@zip.com.au>.
+ *
+ * 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.
+ */
+
+#ifndef MPLAYER_AF_FORMAT_ULAW_H
+#define MPLAYER_AF_FORMAT_ULAW_H
+
+#include <inttypes.h>
+
+#include "af.h"
+// Conversion tables (the function are below)
+static short ulaw_decode[128] =
+{ -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
+ -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
+ -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
+ -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
+ -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+ -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+ -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+ -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+ -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
+ -876, -844, -812, -780, -748, -716, -684, -652,
+ -620, -588, -556, -524, -492, -460, -428, -396,
+ -372, -356, -340, -324, -308, -292, -276, -260,
+ -244, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, 0,
+} ;
+
+static unsigned char ulaw_encode[8193] =
+{ 0xFF, 0xFE, 0xFE, 0xFD, 0xFD, 0xFC, 0xFC, 0xFB, 0xFB, 0xFA, 0xFA, 0xF9,
+ 0xF9, 0xF8, 0xF8, 0xF7, 0xF7, 0xF6, 0xF6, 0xF5, 0xF5, 0xF4, 0xF4, 0xF3,
+ 0xF3, 0xF2, 0xF2, 0xF1, 0xF1, 0xF0, 0xF0, 0xEF, 0xEF, 0xEF, 0xEF, 0xEE,
+ 0xEE, 0xEE, 0xEE, 0xED, 0xED, 0xED, 0xED, 0xEC, 0xEC, 0xEC, 0xEC, 0xEB,
+ 0xEB, 0xEB, 0xEB, 0xEA, 0xEA, 0xEA, 0xEA, 0xE9, 0xE9, 0xE9, 0xE9, 0xE8,
+ 0xE8, 0xE8, 0xE8, 0xE7, 0xE7, 0xE7, 0xE7, 0xE6, 0xE6, 0xE6, 0xE6, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE4, 0xE4, 0xE4, 0xE4, 0xE3, 0xE3, 0xE3, 0xE3, 0xE2,
+ 0xE2, 0xE2, 0xE2, 0xE1, 0xE1, 0xE1, 0xE1, 0xE0, 0xE0, 0xE0, 0xE0, 0xDF,
+ 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE,
+ 0xDE, 0xDE, 0xDE, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDC,
+ 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+ 0xDB, 0xDB, 0xDB, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xD9,
+ 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+ 0xD8, 0xD8, 0xD8, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD6,
+ 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+ 0xD5, 0xD5, 0xD5, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD3,
+ 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD0,
+ 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF,
+ 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCE,
+ 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE,
+ 0xCE, 0xCE, 0xCE, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD,
+ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCB,
+ 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB,
+ 0xCB, 0xCB, 0xCB, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA,
+ 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9,
+ 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC8,
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+ 0xC8, 0xC8, 0xC8, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7,
+ 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC5,
+ 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5,
+ 0xC5, 0xC5, 0xC5, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4,
+ 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC2,
+ 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2,
+ 0xC2, 0xC2, 0xC2, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1,
+ 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xBF,
+ 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+ 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+ 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+ 0xBE, 0xBE, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+ 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+ 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBC,
+ 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+ 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+ 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xBB, 0xBB, 0xBB, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+ 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+ 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xB9,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+ 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+ 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+ 0xB8, 0xB8, 0xB8, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+ 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+ 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6,
+ 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+ 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+ 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+ 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+ 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+ 0xB5, 0xB5, 0xB5, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+ 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+ 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB3,
+ 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+ 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+ 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+ 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+ 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+ 0xB2, 0xB2, 0xB2, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+ 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+ 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB0,
+ 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+ 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+ 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00
+} ;
+
+
+/* Convert from ulaw to signd int8 to signed int32 or float */
+static int from_ulaw(void* in, void* out, int len, int bps, int format)
+{
+ register int i;
+ // Make sure the input parametrs are OK
+ if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US))
+ return AF_ERROR;
+
+ // Convert to int or to float
+ if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){
+ switch(bps){
+ case(1):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((int8_t*)out)[i] = (-1 * ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8;
+ else
+ ((int8_t*)out)[i] = (ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8;
+ }
+ break;
+ case(2):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((int16_t*)out)[i] = -1 * ulaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ else
+ ((int16_t*)out)[i] = ulaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ }
+ break;
+ case(4):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((int32_t*)out)[i] = (-1 * ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16;
+ else
+ ((int32_t*)out)[i] = (ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16;
+ }
+ break;
+ default:
+ return AF_ERROR;
+ }
+ }
+ else{
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] & 0x80)
+ ((float*)out)[i] = -1.0/32768.0 * (float)ulaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ else
+ ((float*)out)[i] = +1.0/32768.0 * (float)ulaw_decode[(((int8_t*)in)[i]) & 0x7F];
+ }
+ }
+ return AF_OK;
+}
+
+/* Convert from signed int8 to signed int32 or float to ulaw */
+static int to_ulaw(void* in, void* out, int len, int bps, int format)
+{
+ register int i;
+ // Make sure the input parametrs are OK
+ if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US))
+ return AF_ERROR;
+
+ // Convert from int or to float
+ if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){
+ switch(bps){
+ case(1):
+ for(i=0;i<len;i++){
+ if(((int8_t*)in)[i] >= 0)
+ ((int8_t*)out)[i] = ulaw_encode[((int8_t*)in)[i] << 6];
+ else
+ ((int8_t*)out)[i] = 0x7F & ulaw_encode[-((int8_t*)in)[i] << 6];
+ }
+ break;
+ case(2):
+ for(i=0;i<len;i++){
+ if(((int16_t*)in)[i] >= 0)
+ ((int8_t*)out)[i] = ulaw_encode[((int16_t*)in)[i] / 4];
+ else
+ ((int8_t*)out)[i] = 0x7F & ulaw_encode[((int16_t*)in)[i] / -4];
+ }
+ break;
+ case(4):
+ for(i=0;i<len;i++){
+ if(((int32_t*)in)[i] >= 0)
+ ((int8_t*)out)[i] = ulaw_encode[((int32_t*)in)[i] >> (16 + 2)];
+ else
+ ((int8_t*)out)[i] = 0x7F & ulaw_encode[-((int32_t*)in)[i] >> (16 + 2)];
+ }
+ break;
+ default:
+ return AF_ERROR;
+ }
+ }
+ else{
+ for(i=0;i<len;i++){
+ if(((float*)in)[i] >= 0)
+ ((int8_t*)out)[i] = ulaw_encode[(int)(32767.0/4.0 * ((float*)in)[i])];
+ else
+ ((int8_t*)out)[i] = 0x7F & ulaw_encode[(int)(-32767.0/4.0 * ((float*)in)[i])];
+ }
+ }
+ return AF_OK;
+}
+
+#endif /* MPLAYER_AF_FORMAT_ULAW_H */
diff --git a/audio/filter/af_hrtf.c b/audio/filter/af_hrtf.c
new file mode 100644
index 0000000000..4f5eedb29d
--- /dev/null
+++ b/audio/filter/af_hrtf.c
@@ -0,0 +1,670 @@
+/*
+ * Experimental audio filter that mixes 5.1 and 5.1 with matrix
+ * encoded rear channels into headphone signal using FIR filtering
+ * with HRTF.
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+
+#include <math.h>
+#include <libavutil/common.h>
+
+#include "af.h"
+#include "dsp.h"
+
+/* HRTF filter coefficients and adjustable parameters */
+#include "af_hrtf.h"
+
+typedef struct af_hrtf_s {
+ /* Lengths */
+ int dlbuflen, hrflen, basslen;
+ /* L, C, R, Ls, Rs channels */
+ float *lf, *rf, *lr, *rr, *cf, *cr;
+ const float *cf_ir, *af_ir, *of_ir, *ar_ir, *or_ir, *cr_ir;
+ int cf_o, af_o, of_o, ar_o, or_o, cr_o;
+ /* Bass */
+ float *ba_l, *ba_r;
+ float *ba_ir;
+ /* Whether to matrix decode the rear center channel */
+ int matrix_mode;
+ /* How to decode the input:
+ 0 = 5/5+1 channels
+ 1 = 2 channels
+ 2 = matrix encoded 2 channels */
+ int decode_mode;
+ /* Full wave rectified (FWR) amplitudes and gain used to steer the
+ active matrix decoding of front channels (variable names
+ lpr/lmr means Lt + Rt, Lt - Rt) */
+ float l_fwr, r_fwr, lpr_fwr, lmr_fwr;
+ float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain;
+ /* Matrix input decoding require special FWR buffer, since the
+ decoding is done in place. */
+ float *fwrbuf_l, *fwrbuf_r, *fwrbuf_lr, *fwrbuf_rr;
+ /* Rear channel delay buffer for matrix decoding */
+ float *rear_dlbuf;
+ /* Full wave rectified amplitude and gain used to steer the active
+ matrix decoding of center rear channel */
+ float lr_fwr, rr_fwr, lrprr_fwr, lrmrr_fwr;
+ float adapt_lr_gain, adapt_rr_gain;
+ float adapt_lrprr_gain, adapt_lrmrr_gain;
+ /* Cyclic position on the ring buffer */
+ int cyc_pos;
+ int print_flag;
+} af_hrtf_t;
+
+/* Convolution on a ring buffer
+ * nx: length of the ring buffer
+ * nk: length of the convolution kernel
+ * sx: ring buffer
+ * sk: convolution kernel
+ * offset: offset on the ring buffer, can be
+ */
+static float conv(const int nx, const int nk, const float *sx, const float *sk,
+ const int offset)
+{
+ /* k = reminder of offset / nx */
+ int k = offset >= 0 ? offset % nx : nx + (offset % nx);
+
+ if(nk + k <= nx)
+ return af_filter_fir(nk, sx + k, sk);
+ else
+ return af_filter_fir(nk + k - nx, sx, sk + nx - k) +
+ af_filter_fir(nx - k, sx + k, sk);
+}
+
+/* Detect when the impulse response starts (significantly) */
+static int pulse_detect(const float *sx)
+{
+ /* nmax must be the reference impulse response length (128) minus
+ s->hrflen */
+ const int nmax = 128 - HRTFFILTLEN;
+ const float thresh = IRTHRESH;
+ int i;
+
+ for(i = 0; i < nmax; i++)
+ if(fabs(sx[i]) > thresh)
+ return i;
+ return 0;
+}
+
+/* Fuzzy matrix coefficient transfer function to "lock" the matrix on
+ a effectively passive mode if the gain is approximately 1 */
+static inline float passive_lock(float x)
+{
+ const float x1 = x - 1;
+ const float ax1s = fabs(x - 1) * (1.0 / MATAGCLOCK);
+
+ return x1 - x1 / (1 + ax1s * ax1s) + 1;
+}
+
+/* Unified active matrix decoder for 2 channel matrix encoded surround
+ sources */
+static inline void matrix_decode(short *in, const int k, const int il,
+ const int ir, const int decode_rear,
+ const int dlbuflen,
+ float l_fwr, float r_fwr,
+ float lpr_fwr, float lmr_fwr,
+ float *adapt_l_gain, float *adapt_r_gain,
+ float *adapt_lpr_gain, float *adapt_lmr_gain,
+ float *lf, float *rf, float *lr,
+ float *rr, float *cf)
+{
+ const int kr = (k + MATREARDELAY) % dlbuflen;
+ float l_gain = (l_fwr + r_fwr) /
+ (1 + l_fwr + l_fwr);
+ float r_gain = (l_fwr + r_fwr) /
+ (1 + r_fwr + r_fwr);
+ /* The 2nd axis has strong gain fluctuations, and therefore require
+ limits. The factor corresponds to the 1 / amplification of (Lt
+ - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
+ dialogues). It should be bigger than -12 dB to prevent
+ distortion. */
+ float lmr_lim_fwr = lmr_fwr > M9_03DB * lpr_fwr ?
+ lmr_fwr : M9_03DB * lpr_fwr;
+ float lpr_gain = (lpr_fwr + lmr_lim_fwr) /
+ (1 + lpr_fwr + lpr_fwr);
+ float lmr_gain = (lpr_fwr + lmr_lim_fwr) /
+ (1 + lmr_lim_fwr + lmr_lim_fwr);
+ float lmr_unlim_gain = (lpr_fwr + lmr_fwr) /
+ (1 + lmr_fwr + lmr_fwr);
+ float lpr, lmr;
+ float l_agc, r_agc, lpr_agc, lmr_agc;
+ float f, d_gain, c_gain, c_agc_cfk;
+
+#if 0
+ static int counter = 0;
+ static FILE *fp_out;
+
+ if(counter == 0)
+ fp_out = fopen("af_hrtf.log", "w");
+ if(counter % 240 == 0)
+ fprintf(fp_out, "%g %g %g %g %g ", counter * (1.0 / 48000),
+ l_gain, r_gain, lpr_gain, lmr_gain);
+#endif
+
+ /*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
+ /* AGC adaption */
+ d_gain = (fabs(l_gain - *adapt_l_gain) +
+ fabs(r_gain - *adapt_r_gain)) * 0.5;
+ f = d_gain * (1.0 / MATAGCTRIG);
+ f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
+ *adapt_l_gain = (1 - f) * *adapt_l_gain + f * l_gain;
+ *adapt_r_gain = (1 - f) * *adapt_r_gain + f * r_gain;
+ /* Matrix */
+ l_agc = in[il] * passive_lock(*adapt_l_gain);
+ r_agc = in[ir] * passive_lock(*adapt_r_gain);
+ cf[k] = (l_agc + r_agc) * M_SQRT1_2;
+ if(decode_rear) {
+ lr[kr] = rr[kr] = (l_agc - r_agc) * M_SQRT1_2;
+ /* Stereo rear channel is steered with the same AGC steering as
+ the decoding matrix. Note this requires a fast updating AGC
+ at the order of 20 ms (which is the case here). */
+ lr[kr] *= (l_fwr + l_fwr) /
+ (1 + l_fwr + r_fwr);
+ rr[kr] *= (r_fwr + r_fwr) /
+ (1 + l_fwr + r_fwr);
+ }
+
+ /*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
+ lpr = (in[il] + in[ir]) * M_SQRT1_2;
+ lmr = (in[il] - in[ir]) * M_SQRT1_2;
+ /* AGC adaption */
+ d_gain = fabs(lmr_unlim_gain - *adapt_lmr_gain);
+ f = d_gain * (1.0 / MATAGCTRIG);
+ f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
+ *adapt_lpr_gain = (1 - f) * *adapt_lpr_gain + f * lpr_gain;
+ *adapt_lmr_gain = (1 - f) * *adapt_lmr_gain + f * lmr_gain;
+ /* Matrix */
+ lpr_agc = lpr * passive_lock(*adapt_lpr_gain);
+ lmr_agc = lmr * passive_lock(*adapt_lmr_gain);
+ lf[k] = (lpr_agc + lmr_agc) * M_SQRT1_2;
+ rf[k] = (lpr_agc - lmr_agc) * M_SQRT1_2;
+
+ /*** CENTER FRONT CANCELLATION ***/
+ /* A heuristic approach exploits that Lt + Rt gain contains the
+ information about Lt, Rt correlation. This effectively reshapes
+ the front and rear "cones" to concentrate Lt + Rt to C and
+ introduce Lt - Rt in L, R. */
+ /* 0.67677 is the emprical lower bound for lpr_gain. */
+ c_gain = 8 * (*adapt_lpr_gain - 0.67677);
+ c_gain = c_gain > 0 ? c_gain : 0;
+ /* c_gain should not be too high, not even reaching full
+ cancellation (~ 0.50 - 0.55 at current AGC implementation), or
+ the center will s0und too narrow. */
+ c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
+ c_agc_cfk = c_gain * cf[k];
+ lf[k] -= c_agc_cfk;
+ rf[k] -= c_agc_cfk;
+ cf[k] += c_agc_cfk + c_agc_cfk;
+#if 0
+ if(counter % 240 == 0)
+ fprintf(fp_out, "%g %g %g %g %g\n",
+ *adapt_l_gain, *adapt_r_gain,
+ *adapt_lpr_gain, *adapt_lmr_gain,
+ c_gain);
+ counter++;
+#endif
+}
+
+static inline void update_ch(af_hrtf_t *s, short *in, const int k)
+{
+ const int fwr_pos = (k + FWRDURATION) % s->dlbuflen;
+ /* Update the full wave rectified total amplitude */
+ /* Input matrix decoder */
+ if(s->decode_mode == HRTF_MIX_MATRIX2CH) {
+ s->l_fwr += abs(in[0]) - fabs(s->fwrbuf_l[fwr_pos]);
+ s->r_fwr += abs(in[1]) - fabs(s->fwrbuf_r[fwr_pos]);
+ s->lpr_fwr += abs(in[0] + in[1]) -
+ fabs(s->fwrbuf_l[fwr_pos] + s->fwrbuf_r[fwr_pos]);
+ s->lmr_fwr += abs(in[0] - in[1]) -
+ fabs(s->fwrbuf_l[fwr_pos] - s->fwrbuf_r[fwr_pos]);
+ }
+ /* Rear matrix decoder */
+ if(s->matrix_mode) {
+ s->lr_fwr += abs(in[2]) - fabs(s->fwrbuf_lr[fwr_pos]);
+ s->rr_fwr += abs(in[3]) - fabs(s->fwrbuf_rr[fwr_pos]);
+ s->lrprr_fwr += abs(in[2] + in[3]) -
+ fabs(s->fwrbuf_lr[fwr_pos] + s->fwrbuf_rr[fwr_pos]);
+ s->lrmrr_fwr += abs(in[2] - in[3]) -
+ fabs(s->fwrbuf_lr[fwr_pos] - s->fwrbuf_rr[fwr_pos]);
+ }
+
+ switch (s->decode_mode) {
+ case HRTF_MIX_51:
+ /* 5/5+1 channel sources */
+ s->lf[k] = in[0];
+ s->cf[k] = in[4];
+ s->rf[k] = in[1];
+ s->fwrbuf_lr[k] = s->lr[k] = in[2];
+ s->fwrbuf_rr[k] = s->rr[k] = in[3];
+ break;
+ case HRTF_MIX_MATRIX2CH:
+ /* Matrix encoded 2 channel sources */
+ s->fwrbuf_l[k] = in[0];
+ s->fwrbuf_r[k] = in[1];
+ matrix_decode(in, k, 0, 1, 1, s->dlbuflen,
+ s->l_fwr, s->r_fwr,
+ s->lpr_fwr, s->lmr_fwr,
+ &(s->adapt_l_gain), &(s->adapt_r_gain),
+ &(s->adapt_lpr_gain), &(s->adapt_lmr_gain),
+ s->lf, s->rf, s->lr, s->rr, s->cf);
+ break;
+ case HRTF_MIX_STEREO:
+ /* Stereo sources */
+ s->lf[k] = in[0];
+ s->rf[k] = in[1];
+ s->cf[k] = s->lr[k] = s->rr[k] = 0;
+ break;
+ }
+
+ /* We need to update the bass compensation delay line, too. */
+ s->ba_l[k] = in[0] + in[4] + in[2];
+ s->ba_r[k] = in[4] + in[1] + in[3];
+}
+
+/* Initialization and runtime control */
+static int control(struct af_instance *af, int cmd, void* arg)
+{
+ af_hrtf_t *s = af->setup;
+ int test_output_res;
+ char mode;
+
+ switch(cmd) {
+ case AF_CONTROL_REINIT:
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ if(af->data->rate != 48000) {
+ // automatic samplerate adjustment in the filter chain
+ // is not yet supported.
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[hrtf] ERROR: Sampling rate is not 48000 Hz (%d)!\n",
+ af->data->rate);
+ return AF_ERROR;
+ }
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+ if(af->data->nch == 2) {
+ /* 2 channel input */
+ if(s->decode_mode != HRTF_MIX_MATRIX2CH) {
+ /* Default behavior is stereo mixing. */
+ s->decode_mode = HRTF_MIX_STEREO;
+ }
+ }
+ else if (af->data->nch < 5)
+ af->data->nch = 5;
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ test_output_res = af_test_output(af, (struct mp_audio*)arg);
+ af->mul = 2.0 / af->data->nch;
+ // after testing input set the real output format
+ af->data->nch = 2;
+ s->print_flag = 1;
+ return test_output_res;
+ case AF_CONTROL_COMMAND_LINE:
+ sscanf((char*)arg, "%c", &mode);
+ switch(mode) {
+ case 'm':
+ /* Use matrix rear decoding. */
+ s->matrix_mode = 1;
+ break;
+ case 's':
+ /* Input needs matrix decoding. */
+ s->decode_mode = HRTF_MIX_MATRIX2CH;
+ break;
+ case '0':
+ s->matrix_mode = 0;
+ break;
+ default:
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[hrtf] Mode is neither 'm', 's', nor '0' (%c).\n",
+ mode);
+ return AF_ERROR;
+ }
+ s->print_flag = 1;
+ return AF_OK;
+ }
+
+ return AF_UNKNOWN;
+}
+
+/* Deallocate memory */
+static void uninit(struct af_instance *af)
+{
+ if(af->setup) {
+ af_hrtf_t *s = af->setup;
+
+ free(s->lf);
+ free(s->rf);
+ free(s->lr);
+ free(s->rr);
+ free(s->cf);
+ free(s->cr);
+ free(s->ba_l);
+ free(s->ba_r);
+ free(s->ba_ir);
+ free(s->fwrbuf_l);
+ free(s->fwrbuf_r);
+ free(s->fwrbuf_lr);
+ free(s->fwrbuf_rr);
+ free(af->setup);
+ }
+ if(af->data)
+ free(af->data->audio);
+ free(af->data);
+}
+
+/* Filter data through filter
+
+Two "tricks" are used to compensate the "color" of the KEMAR data:
+
+1. The KEMAR data is refiltered to ensure that the front L, R channels
+on the same side of the ear are equalized (especially in the high
+frequencies).
+
+2. A bass compensation is introduced to ensure that 0-200 Hz are not
+damped (without any real 3D acoustical image, however).
+*/
+static struct mp_audio* play(struct af_instance *af, struct mp_audio *data)
+{
+ af_hrtf_t *s = af->setup;
+ short *in = data->audio; // Input audio data
+ short *out = NULL; // Output audio data
+ short *end = in + data->len / sizeof(short); // Loop end
+ float common, left, right, diff, left_b, right_b;
+ const int dblen = s->dlbuflen, hlen = s->hrflen, blen = s->basslen;
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af, data))
+ return NULL;
+
+ if(s->print_flag) {
+ s->print_flag = 0;
+ switch (s->decode_mode) {
+ case HRTF_MIX_51:
+ mp_msg(MSGT_AFILTER, MSGL_INFO,
+ "[hrtf] Using HRTF to mix %s discrete surround into "
+ "L, R channels\n", s->matrix_mode ? "5+1" : "5");
+ break;
+ case HRTF_MIX_STEREO:
+ mp_msg(MSGT_AFILTER, MSGL_INFO,
+ "[hrtf] Using HRTF to mix stereo into "
+ "L, R channels\n");
+ break;
+ case HRTF_MIX_MATRIX2CH:
+ mp_msg(MSGT_AFILTER, MSGL_INFO,
+ "[hrtf] Using active matrix to decode 2 channel "
+ "input, HRTF to mix %s matrix surround into "
+ "L, R channels\n", "3/2");
+ break;
+ default:
+ mp_msg(MSGT_AFILTER, MSGL_WARN,
+ "[hrtf] bogus decode_mode: %d\n", s->decode_mode);
+ break;
+ }
+
+ if(s->matrix_mode)
+ mp_msg(MSGT_AFILTER, MSGL_INFO,
+ "[hrtf] Using active matrix to decode rear center "
+ "channel\n");
+ }
+
+ out = af->data->audio;
+
+ /* MPlayer's 5 channel layout (notation for the variable):
+ *
+ * 0: L (LF), 1: R (RF), 2: Ls (LR), 3: Rs (RR), 4: C (CF), matrix
+ * encoded: Cs (CR)
+ *
+ * or: L = left, C = center, R = right, F = front, R = rear
+ *
+ * Filter notation:
+ *
+ * CF
+ * OF AF
+ * Ear->
+ * OR AR
+ * CR
+ *
+ * or: C = center, A = same side, O = opposite, F = front, R = rear
+ */
+
+ while(in < end) {
+ const int k = s->cyc_pos;
+
+ update_ch(s, in, k);
+
+ /* Simulate a 7.5 ms -20 dB echo of the center channel in the
+ front channels (like reflection from a room wall) - a kind of
+ psycho-acoustically "cheating" to focus the center front
+ channel, which is normally hard to be perceived as front */
+ s->lf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen];
+ s->rf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen];
+
+ switch (s->decode_mode) {
+ case HRTF_MIX_51:
+ case HRTF_MIX_MATRIX2CH:
+ /* Mixer filter matrix */
+ common = conv(dblen, hlen, s->cf, s->cf_ir, k + s->cf_o);
+ if(s->matrix_mode) {
+ /* In matrix decoding mode, the rear channel gain must be
+ renormalized, as there is an additional channel. */
+ matrix_decode(in, k, 2, 3, 0, s->dlbuflen,
+ s->lr_fwr, s->rr_fwr,
+ s->lrprr_fwr, s->lrmrr_fwr,
+ &(s->adapt_lr_gain), &(s->adapt_rr_gain),
+ &(s->adapt_lrprr_gain), &(s->adapt_lrmrr_gain),
+ s->lr, s->rr, NULL, NULL, s->cr);
+ common +=
+ conv(dblen, hlen, s->cr, s->cr_ir, k + s->cr_o) *
+ M1_76DB;
+ left =
+ ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) +
+ conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) +
+ (conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) +
+ conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o)) *
+ M1_76DB + common);
+ right =
+ ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) +
+ conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) +
+ (conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) +
+ conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o)) *
+ M1_76DB + common);
+ } else {
+ left =
+ ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) +
+ conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) +
+ conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) +
+ conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o) +
+ common);
+ right =
+ ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) +
+ conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) +
+ conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) +
+ conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o) +
+ common);
+ }
+ break;
+ case HRTF_MIX_STEREO:
+ left =
+ ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) +
+ conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o));
+ right =
+ ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) +
+ conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o));
+ break;
+ default:
+ /* make gcc happy */
+ left = 0.0;
+ right = 0.0;
+ break;
+ }
+
+ /* Bass compensation for the lower frequency cut of the HRTF. A
+ cross talk of the left and right channel is introduced to
+ match the directional characteristics of higher frequencies.
+ The bass will not have any real 3D perception, but that is
+ OK (note at 180 Hz, the wavelength is about 2 m, and any
+ spatial perception is impossible). */
+ left_b = conv(dblen, blen, s->ba_l, s->ba_ir, k);
+ right_b = conv(dblen, blen, s->ba_r, s->ba_ir, k);
+ left += (1 - BASSCROSS) * left_b + BASSCROSS * right_b;
+ right += (1 - BASSCROSS) * right_b + BASSCROSS * left_b;
+ /* Also mix the LFE channel (if available) */
+ if(data->nch >= 6) {
+ left += in[5] * M3_01DB;
+ right += in[5] * M3_01DB;
+ }
+
+ /* Amplitude renormalization. */
+ left *= AMPLNORM;
+ right *= AMPLNORM;
+
+ switch (s->decode_mode) {
+ case HRTF_MIX_51:
+ case HRTF_MIX_STEREO:
+ /* "Cheating": linear stereo expansion to amplify the 3D
+ perception. Note: Too much will destroy the acoustic space
+ and may even result in headaches. */
+ diff = STEXPAND2 * (left - right);
+ out[0] = av_clip_int16(left + diff);
+ out[1] = av_clip_int16(right - diff);
+ break;
+ case HRTF_MIX_MATRIX2CH:
+ /* Do attempt any stereo expansion with matrix encoded
+ sources. The L, R channels are already stereo expanded
+ by the steering, any further stereo expansion will sound
+ very unnatural. */
+ out[0] = av_clip_int16(left);
+ out[1] = av_clip_int16(right);
+ break;
+ }
+
+ /* Next sample... */
+ in = &in[data->nch];
+ out = &out[af->data->nch];
+ (s->cyc_pos)--;
+ if(s->cyc_pos < 0)
+ s->cyc_pos += dblen;
+ }
+
+ /* Set output data */
+ data->audio = af->data->audio;
+ data->len = data->len / data->nch * 2;
+ data->nch = 2;
+
+ return data;
+}
+
+static int allocate(af_hrtf_t *s)
+{
+ if ((s->lf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->rf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->lr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->rr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->cf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->cr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->ba_l = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->ba_r = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->fwrbuf_l =
+ malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->fwrbuf_r =
+ malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->fwrbuf_lr =
+ malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ if ((s->fwrbuf_rr =
+ malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
+ return 0;
+}
+
+/* Allocate memory and set function pointers */
+static int af_open(struct af_instance* af)
+{
+ int i;
+ af_hrtf_t *s;
+ float fc;
+
+ af->control = control;
+ af->uninit = uninit;
+ af->play = play;
+ af->mul = 1;
+ af->data = calloc(1, sizeof(struct mp_audio));
+ af->setup = calloc(1, sizeof(af_hrtf_t));
+ if((af->data == NULL) || (af->setup == NULL))
+ return AF_ERROR;
+
+ s = af->setup;
+
+ s->dlbuflen = DELAYBUFLEN;
+ s->hrflen = HRTFFILTLEN;
+ s->basslen = BASSFILTLEN;
+
+ s->cyc_pos = s->dlbuflen - 1;
+ /* With a full (two axis) steering matrix decoder, s->matrix_mode
+ should not be enabled lightly (it will also steer the Ls, Rs
+ channels). */
+ s->matrix_mode = 0;
+ s->decode_mode = HRTF_MIX_51;
+
+ s->print_flag = 1;
+
+ if (allocate(s) != 0) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Memory allocation error.\n");
+ return AF_ERROR;
+ }
+
+ for(i = 0; i < s->dlbuflen; i++)
+ s->lf[i] = s->rf[i] = s->lr[i] = s->rr[i] = s->cf[i] =
+ s->cr[i] = 0;
+
+ s->lr_fwr =
+ s->rr_fwr = 0;
+
+ s->cf_ir = cf_filt + (s->cf_o = pulse_detect(cf_filt));
+ s->af_ir = af_filt + (s->af_o = pulse_detect(af_filt));
+ s->of_ir = of_filt + (s->of_o = pulse_detect(of_filt));
+ s->ar_ir = ar_filt + (s->ar_o = pulse_detect(ar_filt));
+ s->or_ir = or_filt + (s->or_o = pulse_detect(or_filt));
+ s->cr_ir = cr_filt + (s->cr_o = pulse_detect(cr_filt));
+
+ if((s->ba_ir = malloc(s->basslen * sizeof(float))) == NULL) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Memory allocation error.\n");
+ return AF_ERROR;
+ }
+ fc = 2.0 * BASSFILTFREQ / (float)af->data->rate;
+ if(af_filter_design_fir(s->basslen, s->ba_ir, &fc, LP | KAISER, 4 * M_PI) ==
+ -1) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Unable to design low-pass "
+ "filter.\n");
+ return AF_ERROR;
+ }
+ for(i = 0; i < s->basslen; i++)
+ s->ba_ir[i] *= BASSGAIN;
+
+ return AF_OK;
+}
+
+/* Description of this filter */
+struct af_info af_info_hrtf = {
+ "HRTF Headphone",
+ "hrtf",
+ "ylai",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_hrtf.h b/audio/filter/af_hrtf.h
new file mode 100644
index 0000000000..887310b57d
--- /dev/null
+++ b/audio/filter/af_hrtf.h
@@ -0,0 +1,511 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_AF_HRTF_H
+#define MPLAYER_AF_HRTF_H
+
+#define HRTF_MIX_51 0
+#define HRTF_MIX_STEREO 1
+#define HRTF_MIX_MATRIX2CH 2
+
+/* Amplitude scaling factors */
+#define M17_0DB 0.1414213562
+#define M9_03DB 0.3535533906
+#define M6_99DB 0.4472135955
+#define M4_77DB 0.5773502692
+#define M3_01DB 0.7071067812
+#define M1_76DB 0.8164965809
+
+#define DELAYBUFLEN 1024 /* Length of the delay buffer */
+#define HRTFFILTLEN 64 /* HRTF filter length */
+#define IRTHRESH 0.001 /* Impulse response pruning thresh. */
+
+#define AMPLNORM M6_99DB /* Overall amplitude renormalization */
+
+#define BASSFILTFREQ 180 /* Bass compensation filter cut (Hz) */
+#define BASSFILTLEN 193 /* Bass compensation filter length */
+#define BASSGAIN M_SQRT2 /* Bass compensation gain */
+#define BASSCROSS 0.35 /* Bass cross talk */
+
+#define FWRDURATION 240 /* FWR average duration (samples) */
+#define MATREARDELAY 720 /* Matrix mode rear delay (samples) */
+
+#define MATAGCTRIG 8.0 /* (Fuzzy) AGC trigger */
+#define MATAGCDECAY 1.0 /* AGC baseline decay rate (1/samp.) */
+#define MATAGCLOCK 0.2 /* AGC range (around 1) where the
+ matrix behaves passively */
+#define MATCOMPGAIN 0.37 /* Cross talk compensation gain,
+ 0.50 - 0.55 is full cancellation. */
+
+#define CFECHODELAY 360 /* Center front echo delay (samples) */
+#define CFECHOAMPL M17_0DB /* Center front echo amplitude */
+
+#define STEXPAND2 0.07 /* Stereo expansion / 2 */
+
+/* Head related impulse response (HRIR) derived from KEMAR measurement
+ data by Bill Gardner <billg@media.mit.edu> and Keith Martin
+ <kdm@media.mit.edu>
+
+ URL: http://sound.media.mit.edu/KEMAR.html
+
+ Distributed under GPL with authors' permission
+*/
+
+/* EQUALIZED KEMAR HRIR
+
+How to generate these data:
+
+1. You need the MIT Media Lab's KEMAR data, read it into an software
+ capable of signal/time series analysis (like Mathematica, Matlab,
+ ...)
+
+2. Construct an equalizing FIR inverse filter by calculating the
+ transfer function of the front, same side impulse response, then
+ take 1 over the absolute magnitude.
+
+3. Cut the poles in the inverse filter's transfer function
+ specification by limiting the amplification to 2.5 (note, this
+ number assumes that you have correct signal processing
+ normalization of the Fourier transform).
+
+4. Design the FIR inverse filter by calculating the inverse Fourier
+ transform, then chopping the coefficients down to a reasonable
+ number (N = 15 is used here), apply the Kaiser-Bessel window (alpha
+ = 2 is used here). Note the objective is remove the color bias
+ only (as if you are using an equalizer), _not_ to do a full inverse
+ filtering. (Note: beta = pi*alpha in other notation.)
+
+ For N = 15, alpha = 2, you should get the following impulse
+ response:
+
+ 0.001001558668605168, 0.00698457265741865, 0.040453643039829436,
+ 0.012230541722147855, -0.11939760844854072, 0.16468099899755967,
+ -0.30297563073747436, 1.3140211791355982, -0.30297563073747436,
+ 0.16468099899755967, -0.11939760844854072, 0.012230541722147855,
+ 0.040453643039829436, 0.00698457265741865, 0.001001558668605168
+
+5. Linearly convolve all KEMAR HRIR with this inverse filter.
+
+6. Resample from 44.1 kHz sampling frequency to 48 kHz.
+*/
+
+/* Center front (-5 degree) - not 0 degree in order to create a clear
+ front image from a finite distance */
+static const float cf_filt[128] = {
+ -0.00008638082319075036, 0.0003198059946385229,
+ -0.0005010631339162132, 0.0011424741331126876,
+ -0.001584220794688753, 0.001742715363246275,
+ -0.0011080796626780694, 0.0001651829990860167,
+ 0.005235028337314985, 0.0035223828473357776,
+ 0.010057681388431303, -0.033469432129545514,
+ 0.013391253316233523, 0.004858462839827063,
+ 0.08172161220103627, 0.26158596134500023,
+ -0.12420314583323326, -0.14298458356097565,
+ 0.14421897280453896, -0.1635792507629016,
+ -0.02187136722480014, 0.2426863044711817,
+ 0.07229814207917194, 0.0942742651913879,
+ 0.29856830878076834, 0.2944146162057754,
+ -0.12122157003421209, -0.19640092165631157,
+ 0.11623836502034968, -0.05794027397995521,
+ -0.34313138341973776, -0.19487516249168105,
+ 0.010118993953802401, -0.09460218797710966,
+ -0.16761521117359582, 0.004154461610153861,
+ 0.052768641758969316, -0.00041823982226147407,
+ 0.021634960445143514, 0.07562793486871108,
+ 0.08895407129506479, 0.039857755093416214,
+ 0.044257936180422945, 0.061557584906101664,
+ 0.015547268541895703, -0.023908191934932484,
+ 0.009498030443468223, 0.03816269003221337,
+ -0.009820500607303615, -0.042003975527908084,
+ -0.03335447117311547, -0.029294510859746596,
+ -0.05212623136198511, -0.073427547153205,
+ -0.061190797824958836, -0.04620925059966413,
+ -0.04204619420702159, -0.02331915902615157,
+ 0.00211481411477094, 0.00852563995740107,
+ 0.008766809731743399, 0.008666632180812078,
+ 0.018279202191625352, 0.02924332751289675,
+ 0.022293148257836494, 0.012362146008584188,
+ 0.008572582458807008, 0.006491370763597344,
+ -0.0019366944997535774, -0.006318669309634434,
+ -0.006457921690218669, -0.015050265524669974,
+ -0.02110660282616213, -0.017027809096377904,
+ -0.01651052305334348, -0.022770064150046673,
+ -0.01999875754219472, -0.012294792027337775,
+ -0.011506057031057188, -0.011448970577312903,
+ -0.004823572302580925, 0.0022451134042777883,
+ 0.004145473526859826, 0.005629030064546135,
+ 0.008588029213398785, 0.010092048834844231,
+ 0.007182013245552008, 0.0014600979508720656,
+ -0.0038314646272511756, -0.003443901997881347,
+ -0.0029483418254804047, -0.007609357112679647,
+ -0.006518368948030822, -0.004495803701497202,
+ -0.007109113004849672, -0.008346237278084265,
+ -0.005560847336252453, -0.002993453167553188,
+ -0.005122897816824269, -0.004389782626604215,
+ -0.0010912633695218108, -0.0019712029474458835,
+ -0.005870162265802235, -0.005626159534954128,
+ -0.00027254977910844407, 0.0013794425431633785,
+ -0.0005919083190430062, -0.0007861203545416682,
+ -0.0007049560240893946, -0.0032720188494468868,
+ -0.004460645567968504, -0.0032018528193571696,
+ -0.0030579229375062105, -0.003593998902656612,
+ -0.0038032977997776445, -0.004613776010454773,
+ -0.0059796549143736845, -0.00420126194319768,
+ -0.0012374419948287222, 0.0008572699213050608,
+ 0.0021490971020081094, 0.00047295283198381995,
+ -0.0009670277915884887, -0.001354440866080231,
+ -0.002962902746547851, -0.00533935813338141,
+ -0.005469203016468759, -0.004355784273189485
+};
+/* Front, same side (30 degree) */
+static const float af_filt[128] = {
+ -0.004140580614755493, 0.005790934614385445,
+ 0.003318916682081112, 0.014257145544366063,
+ 0.007328442487127339, -0.06550381777876194,
+ 0.03502225818161845, -0.013090579770708259,
+ 0.2517776798694195, 0.420770489950659,
+ -0.3418854608834852, -0.24371032493696737,
+ 0.04901356150030018, -0.1778083521632833,
+ 0.26448004245714163, 0.23245199964546834,
+ 0.033053145803936305, 0.46811222821062415,
+ 0.5359265986255611, -0.011912195468533787,
+ -0.39763432601411647, -0.034482864386898314,
+ 0.029445398240649626, -0.3850940407863262,
+ -0.3272328478175581, -0.14701421403616477,
+ -0.08522137400169517, -0.14936851633336035,
+ -0.09432605283433723, 0.0991200405937827,
+ 0.011075012089917331, -0.0051036489980274685,
+ 0.0933903289749412, 0.1344189369609565,
+ 0.10507466913017807, 0.04240159324684365,
+ 0.06559270110638656, 0.026984119875617524,
+ -0.03359846103238096, -0.018000197099174275,
+ 0.042031818548178244, 0.03849039666888434,
+ -0.02450829674011345, -0.03407882403088576,
+ -0.029230189282961977, -0.046964865291761734,
+ -0.09458258700116245, -0.1527349330901158,
+ -0.15411577687826097, -0.08761679790956928,
+ -0.033623549089171874, -0.007204768531481949,
+ 0.008273425020444852, 0.021368717994908505,
+ 0.04366608267875025, 0.05660907333076205,
+ 0.06775726495503939, 0.05621881735271431,
+ 0.03576231950669927, 0.02500825834889175,
+ 0.015423811076054601, 0.007903258334503761,
+ -0.0053873014137761945, -0.006987955469434698,
+ -0.012027972007598602, -0.025228281243816594,
+ -0.026225091797257318, -0.023809293997344882,
+ -0.03250172017712295, -0.03195696301067249,
+ -0.01784813952189948, -0.01663267233760342,
+ -0.016184530450468065, -0.011659883749357463,
+ -0.0035378511240219163, -0.0005485800790443406,
+ 0.0018432660108168625, 0.011634844139907534,
+ 0.018333603402051105, 0.020447379185133056,
+ 0.00850783664147828, 0.0004694148911037838,
+ -0.0017047130409786676, -0.0022409152834483997,
+ -0.000860472174892845, -0.004111075059198666,
+ -0.003527843382056666, -0.009640160874903018,
+ -0.01750044574231376, -0.015613389403672443,
+ -0.010790028120953001, -0.0095313499955768,
+ -0.007469721416726809, -0.0019186578145117315,
+ -0.00014977322572890802, -0.0029803838028179728,
+ -0.006520567233727221, 0.000035015132033882596,
+ 0.009245098100543752, 0.009896930052308656,
+ 0.008316744929565786, 0.004575207140193997,
+ -0.0000647420103997081, -0.004502916832871627,
+ -0.004225962213251224, -0.002886014126381486,
+ -0.006416834142585976, -0.007156609995423569,
+ -0.008840274447579472, -0.01441763751386817,
+ -0.015435817484659574, -0.00924487254924743,
+ -0.0021571721940235205, 0.0028540722992305453,
+ 0.00273577475088536, -0.000788412365513805,
+ -0.0032650029728365907, -0.003880217646231338,
+ -0.0035302087299613778, -0.0038531436176586246,
+ -0.0011921632190514074, -0.0020722967099011938,
+ -0.004985351145629344, -0.0042375588844648735,
+ -0.003030360463006021, -0.0014161075428041471,
+ -0.0005083025643192044, 0.00035096963769606926
+};
+/* Front, opposite (-30 degree) */
+static const float of_filt[128] = {
+ -0.000013472538374193126, -0.00008048061877079751,
+ 0.000043927265781258155, -0.000017931700794858892,
+ -0.000034774602476112886, -0.00009576223008735474,
+ 0.0001557797638630691, -0.00018742885883751094,
+ 0.00026512448626705716, -0.0001451040203319678,
+ -0.00008263233117758043, 0.0006486245853639179,
+ -0.0010631408451846698, 0.0026571994100746143,
+ 0.0014179177997092787, 0.0062326502956616256,
+ -0.008194149324545333, -0.006568029415878379,
+ 0.009538759710818582, 0.012309193558632693,
+ 0.12336638055838955, 0.046164307101829005,
+ -0.10228706407884815, 0.04047687260345798,
+ -0.00296595313977046, -0.07057949208414134,
+ 0.08172114840714612, 0.08736490764127891,
+ 0.05105250431333021, 0.11627179512747428,
+ 0.20717888490340705, 0.09375052213570291,
+ -0.09784374168330194, -0.010493571845901443,
+ 0.053131894303891716, -0.10157443971694806,
+ -0.16264032634244974, -0.05402369511361273,
+ -0.0274403608654217, -0.09860277022495063,
+ -0.06841875821090282, -0.004434574400066223,
+ -0.0005222661652743502, -0.006231881259827263,
+ 0.014410397820340159, 0.04885649512730243,
+ 0.04361962569042684, 0.03399214029009391,
+ 0.04961073933475931, 0.04067325604132289,
+ 0.007850647519227257, 0.004564440466905299,
+ 0.02257107958021618, 0.008183791928884486,
+ -0.014913479343180557, -0.018685938460856224,
+ -0.01745737397226911, -0.02327177054233603,
+ -0.03723048632685227, -0.044739390162299685,
+ -0.042651220125613766, -0.03730017561004743,
+ -0.029039465434276192, -0.01885087458914294,
+ -0.01207127752277769, -0.006779800724164512,
+ -0.001930416967444157, 0.000029454577995528385,
+ 0.0013822760965755472, 0.0014799128583230202,
+ 0.0002068200609199832, 0.0022254295286201083,
+ 0.005143858159434566, 0.0018580542060917013,
+ -0.0019426046325146259, -0.0014464042108543495,
+ -0.0034430083560735582, -0.009692758426099499,
+ -0.011840035292593485, -0.010716508855893968,
+ -0.012939889036853034, -0.0121846427935653,
+ -0.006198503315630782, -0.0023186723099380305,
+ -0.002679872498314837, -0.003086020446226295,
+ -0.0015709623347698936, -0.0008147490468332398,
+ -0.0012384575726770983, -0.0005212877089109362,
+ 0.0017707578744906142, 0.001324932723905786,
+ -0.0017023653780617696, -0.0045108927752919425,
+ -0.005422155613096912, -0.0039489323837623835,
+ -0.005295995750506547, -0.00629706566356189,
+ -0.004685732198036754, -0.0048076735568143,
+ -0.005978864279217503, -0.005928999306332966,
+ -0.004187703549017582, -0.003213999896976475,
+ -0.0028068699816073414, -0.0010889703907593833,
+ 0.0003276714243495386, -0.0013015007040186994,
+ -0.003208050402434782, -0.0025115319088208545,
+ -0.0013787553006401076, -0.0018279087370218635,
+ -0.0025904836507747754, -0.002071221947222004,
+ -0.0026424212922485594, -0.0039837031817577,
+ -0.0041635566057380965, -0.004355223489150822,
+ -0.004350395332709937, -0.0036693292471930263,
+ -0.003386384394185026, -0.003972568655001128,
+ -0.004332336840023821, -0.002648767912111827,
+ -0.001384410080218114, -0.0011353792711849466,
+ -0.0013726264946164232, -0.0020075119315034313
+};
+/* Rear, same side (135 degree) */
+static const float ar_filt[128] = {
+ 0.004573315040810066, 0.0013592578059426913,
+ 0.01553271930902704, -0.0002356117224941317,
+ -0.05746098219774702, 0.03430688963370445,
+ 0.00808371687447385, 0.21893535841158596,
+ 0.2984357591724814, -0.3302799746504719,
+ -0.3194029149806245, 0.21633225051331056,
+ 0.24371260938097083, -0.08843705549751085,
+ 0.03939684701343366, 0.45386926431114494,
+ 0.07599118140753386, -0.18114706160474578,
+ 0.285640624686038, 0.4049515236666218,
+ -0.05347890222071792, -0.31464359045319074,
+ -0.1033502246468194, -0.04553593949283157,
+ -0.1880747731157464, -0.13629090230626037,
+ -0.10435789106123239, -0.19818232801888755,
+ -0.16701805476330397, -0.022793111199284,
+ 0.058049696762683685, 0.007048321372693906,
+ -0.002966419183225961, 0.10140569697797763,
+ 0.11648999956673124, 0.05218347182779882,
+ 0.028427001212735392, 0.04151900310166159,
+ -0.0006960604221423734, -0.05898623212226975,
+ -0.03801934531173312, -0.029306970535287986,
+ -0.04549125782835908, -0.0599222718506552,
+ -0.058299618975430116, -0.03765579129720727,
+ -0.03559302657499581, -0.020647901025903054,
+ -0.005720957338744348, -0.0041915732688915545,
+ -0.0011470880098346143, 0.008737404798553,
+ 0.023444168098121512, 0.024204226042172663,
+ 0.01894897166475026, 0.020807655257479588,
+ 0.021570431128040954, 0.006800556178576289,
+ -0.009000089216921362, -0.010969824547067934,
+ -0.0033653428332822374, -0.012676936164668659,
+ -0.026739938673413587, -0.023427869194287573,
+ -0.023302007105117244, -0.023647155590533712,
+ -0.021289317515613106, -0.009120487305069884,
+ 0.0009251551667728967, 0.00004285344125653763,
+ -0.00009042365479456271, 0.00022573242339446494,
+ 0.00720168491586098, 0.007111875505402431,
+ 0.003186514817683482, 0.00810087718334745,
+ 0.012619557025922575, 0.007854726400013397,
+ -0.0024013592881066267, -0.001452457473161119,
+ -0.0025535188366093945, -0.012428911627809337,
+ -0.013729251536694145, -0.0070099675146427344,
+ -0.007165284278706593, -0.01639289295622301,
+ -0.015831795079778305, -0.007305768485523729,
+ -0.003608863157004021, -0.0032640528878698084,
+ 0.0030901263998481944, 0.00749497566124848,
+ 0.002515185532327241, 0.00004840875738621367,
+ 0.0017596043486043966, 0.0046229941553338144,
+ 0.0034259167322926096, 0.003707347634186093,
+ 0.0035584806528586328, -0.0019078936035275198,
+ -0.006057343815214898, -0.0069262470468817,
+ -0.004345020728618624, -0.004177623355574794,
+ -0.005373506556122508, -0.006624933928893836,
+ -0.008679541408588839, -0.010718719681595322,
+ -0.011392246979594496, -0.007893917064389902,
+ -0.0027572935365832536, -0.00006064707149834412,
+ -0.0012512537319656323, -0.0024501501002409786,
+ -0.0022106788572895998, -0.00234124933370301,
+ -0.0008953445167066823, 0.0005393670625637734,
+ -0.00033175600142209297, -0.004023994309351289,
+ -0.008655472335784443, -0.009899957354849682,
+ -0.008664952919996412, -0.00553483124503576,
+ -0.003735336089277662, -0.002754824348643885,
+ -0.0026884314856593368, -0.004084181815125924
+};
+/* Rear, opposite (-135 degree) */
+static const float or_filt[128] = {
+ 0.0001220944028243897, -0.000021785381808441314,
+ 5.823057988603169e-6, -0.00001217768176447613,
+ -0.00006123604397345513, 5.574117262531134e-6,
+ -0.00004935331914366778, 1.1771577934768211e-6,
+ -0.000059236211621095756, 9.503536190497286e-6,
+ -0.0001494445696103564, 0.00012248858284145305,
+ -0.0000963975321456313, 6.017905197665205e-6,
+ 0.00003353395360402643, -0.0001931511015359506,
+ 0.0005113536523931485, -0.0005676652619386114,
+ 0.0012057159755477467, 0.0009370492250339692,
+ 0.004596472288877981, -0.0018831773384237068,
+ -0.008208535225621212, 0.0038178646400751056,
+ 0.008726517739105965, 0.06664363898418262,
+ 0.06788684221502142, -0.04492315162807267,
+ -0.04019906311255255, 0.026203059677375153,
+ 0.013678129114847544, -0.014334962223993527,
+ 0.010141709596167392, 0.11559131576945537,
+ 0.1586402064538425, 0.059975334707967023,
+ 0.004671725963777715, 0.031498678282775874,
+ 0.014338626006524587, -0.014749719448472231,
+ -0.02055508237941379, -0.05407690143992048,
+ -0.07767559836886143, -0.05029091786216801,
+ -0.030808335706574427, -0.03401958135442541,
+ -0.030520368430288967, -0.014168302104259355,
+ 0.011907621259989802, 0.014286081013069,
+ 0.006377467879613449, 0.018546823568277478,
+ 0.028297012771618273, 0.025222339408338186,
+ 0.021931611353415138, 0.019708894333646355,
+ 0.01729258494072014, 0.017468204169564034,
+ 0.009729094845051928, -0.002976992018531901,
+ -0.00956986166277019, -0.016125733548332074,
+ -0.02934094241442545, -0.04133767871051455,
+ -0.043536981145416466, -0.0385966307108608,
+ -0.02784453599342459, -0.018995135307247116,
+ -0.012849534096536747, -0.004437491064613308,
+ 0.00028385411598204655, 0.003776874988516643,
+ 0.008069432041547833, 0.008764754183751848,
+ 0.008020908861878062, 0.006830351461360802,
+ 0.002218330884267235, -0.0020478725582339444,
+ -0.003997428121462543, -0.007066287373515421,
+ -0.00940847412544698, -0.010938998446237963,
+ -0.011775483016151306, -0.011391103919484287,
+ -0.010586061195163017, -0.009842793078929053,
+ -0.007753202010139829, -0.00569213732353025,
+ -0.006506783349722073, -0.005346134281903736,
+ -0.003913089814898934, -0.0036091443854759727,
+ -0.0020328564301266287, 0.00017932870773467683,
+ 0.0032779786679056357, 0.003969695813293966,
+ 0.0020339334412434987, -0.00011345940675415259,
+ -0.0018344103399567666, -0.003556764701666365,
+ -0.004263523639408391, -0.002940568582022133,
+ -0.0034341188272627556, -0.006023399920020824,
+ -0.0077456903203677865, -0.007912219312377842,
+ -0.00625202770436523, -0.00530785086116117,
+ -0.005569722659634311, -0.004664448462594344,
+ -0.0037747773914077747, -0.004175649656985592,
+ -0.004659601521384289, -0.005008602967819641,
+ -0.004730625079902729, -0.0034039554356604146,
+ -0.0017110333873406587, -0.0006091938771510242,
+ -0.0016051679050678297, -0.003312864664007262,
+ -0.004505512715977288, -0.004152222189861692,
+ -0.003218596419678823, -0.0027277806209877343,
+ -0.001715005444317267, -0.0012589960071233749,
+ -0.001852908777923165, -0.002540339553144362
+};
+/* Center rear (180 degree) */
+static const float cr_filt[128] = {
+ -0.00005989110716536726, -0.00022790291829128702,
+ 0.0002659166098971966, -0.0003774772716776257,
+ 0.0004540309551867803, -0.000420238187386368,
+ 0.00025518536450885686, 0.00028285526288953955,
+ -0.001016391007574093, 0.0028634984299063795,
+ 0.0021574799687976045, 0.01035121276682072,
+ -0.010481720917298163, -0.013197198495899292,
+ 0.0031928225328717195, 0.02903137843618603,
+ 0.1632429772511569, 0.1047487989875262,
+ -0.10464685060623742, -0.09260196288035998,
+ -0.007514241993554443, 0.013596249226741712,
+ -0.019876166508450258, 0.1255626123599804,
+ 0.3648170359521724, 0.19458249753223478,
+ -0.04434070930031298, 0.046582528121935265,
+ 0.09484427347230277, -0.03137795311969644,
+ -0.10297437925363695, -0.09351091015917065,
+ -0.1129521092162064, -0.14925322995658827,
+ -0.1231466295584665, -0.06356719756705227,
+ -0.05442277895126282, -0.07568433015661316,
+ -0.023314932828602003, 0.04260950721318558,
+ 0.02249026315598923, 0.02048195669571197,
+ 0.05651342117268278, 0.05885038917623213,
+ 0.03797102097397795, 0.011767394419953451,
+ 0.00560502503429481, 0.005051125343961189,
+ -0.012925933188033823, -0.023918884651306566,
+ -0.013251659441678816, -0.010694772488866284,
+ -0.03080486448617846, -0.03661278237783158,
+ -0.0379227303416262, -0.042189005718490775,
+ -0.026595666344596286, -0.009759025956801257,
+ -0.002064986663513004, -0.002420117028098389,
+ -0.006629991977552491, 0.004619970897631026,
+ 0.019450642967537877, 0.0173521119057514,
+ 0.017641425439988062, 0.02270029598048491,
+ 0.018976431925275348, 0.009299852902290885,
+ -0.001695039371619912, -0.00675162574265618,
+ -0.009380968871003034, -0.011208396125485165,
+ -0.01308640049201482, -0.0165636375633249,
+ -0.022004099870933345, -0.025173458684139286,
+ -0.016918759559175375, -0.00865150653575917,
+ -0.006999929082792643, -0.005454830010518988,
+ -0.0021129521131095317, 0.00018717090054046307,
+ -0.0002864344788569993, 0.0017615225381095569,
+ 0.006985907557802283, 0.010577308310476465,
+ 0.006466104789306027, -0.0014988738575948326,
+ -0.0039669755229277195, -0.0065156971200080235,
+ -0.009343206924192169, -0.0076430644693577495,
+ -0.004395214976600924, -0.003052735340422483,
+ -0.007019103043066595, -0.00974109267696527,
+ -0.007968015032797376, -0.007801513845528344,
+ -0.007535748903681969, -0.003543341967287925,
+ 0.0015083125553729722, 0.0023345972556147025,
+ -0.0010043623069557037, -0.0025295765105203746,
+ -0.0023701840891643634, -0.0005908186035024362,
+ 0.0029826252289082847, 0.004829048542117764,
+ 0.004488360022902081, 0.00002643748103005408,
+ -0.0042100779212597295, -0.006170600558114495,
+ -0.007267149164680168, -0.006825522903494639,
+ -0.006899834372739123, -0.0073493916110062675,
+ -0.009554351265163382, -0.011790297433830197,
+ -0.010645796603734424, -0.0064661575394022106,
+ -0.002026743466524137, -0.0004337034584909932,
+ -0.0011172647031654614, -0.0017947816283674731,
+ -0.00255615052036616, -0.0017721562881944813,
+ -0.0002379619297227554, 0.0007130120121089036
+};
+
+#endif /* MPLAYER_AF_HRTF_H */
diff --git a/audio/filter/af_karaoke.c b/audio/filter/af_karaoke.c
new file mode 100644
index 0000000000..965eb8f40d
--- /dev/null
+++ b/audio/filter/af_karaoke.c
@@ -0,0 +1,98 @@
+/*
+ * simple voice removal filter
+ *
+ * copyright (c) 2006 Reynaldo H. Verdejo Pinochet
+ * Based on code by Alex Beregszaszi for his 'center' filter.
+ *
+ * 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 <string.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+ af->data->format= AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ return af_test_output(af,(struct mp_audio*)arg);
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ float* a = c->audio; // Audio data
+ int len = c->len/4; // Number of samples in current audio block
+ int nch = c->nch; // Number of channels
+ register int i;
+
+ /*
+ FIXME1 add a low band pass filter to avoid suppressing
+ centered bass/drums
+ FIXME2 better calculated* attenuation factor
+ */
+
+ for(i=0;i<len;i+=nch)
+ {
+ a[i] = (a[i] - a[i+1]) * 0.7;
+ a[i+1]=a[i];
+ }
+
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control = control;
+ af->uninit = uninit;
+ af->play = play;
+ af->mul = 1;
+ af->data = calloc(1,sizeof(struct mp_audio));
+
+ if(af->data == NULL)
+ return AF_ERROR;
+
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_karaoke = {
+ "Simple karaoke/voice-removal audio filter",
+ "karaoke",
+ "Reynaldo H. Verdejo Pinochet",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_ladspa.c b/audio/filter/af_ladspa.c
new file mode 100644
index 0000000000..c1b3f24360
--- /dev/null
+++ b/audio/filter/af_ladspa.c
@@ -0,0 +1,915 @@
+/*
+ * LADSPA plugin loader
+ *
+ * Written by Ivo van Poorten <ivop@euronet.nl>
+ * Copyright (C) 2004, 2005
+ *
+ * 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.
+ */
+
+/* ------------------------------------------------------------------------- */
+
+/* Global Includes */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include <dlfcn.h>
+#include <ladspa.h>
+
+/* ------------------------------------------------------------------------- */
+
+/* Local Includes */
+
+#include "af.h"
+
+/* ------------------------------------------------------------------------- */
+
+/* Filter specific data */
+
+typedef struct af_ladspa_s
+{
+ int status; /**< Status of the filter.
+ * Either AF_OK or AF_ERROR
+ * Because MPlayer re-inits audio filters that
+ * _clearly_ returned AF_ERROR anyway, I use this
+ * in play() to skip the processing and return
+ * the data unchanged.
+ */
+
+ int activated; /**< 0 or 1. Activate LADSPA filters only once, even
+ * if the buffers get resized, to avoid a stuttering
+ * filter.
+ */
+
+ char *file;
+ char *label;
+
+ char *myname; /**< It's easy to have a concatenation of file and label */
+
+ void *libhandle;
+ const LADSPA_Descriptor *plugin_descriptor;
+
+ int nports;
+
+ int ninputs;
+ int *inputs;
+
+ int noutputs;
+ int *outputs;
+
+ int ninputcontrols;
+ int *inputcontrolsmap; /**< Map input port number [0-] to actual port */
+ float *inputcontrols;
+
+ int noutputcontrols;
+ int *outputcontrolsmap;
+ float *outputcontrols;
+
+ int nch; /**< number of channels */
+ int bufsize;
+ float **inbufs;
+ float **outbufs;
+ LADSPA_Handle *chhandles;
+
+} af_ladspa_t;
+
+/* ------------------------------------------------------------------------- */
+
+static int af_open(struct af_instance *af);
+static int af_ladspa_malloc_failed(char*);
+
+/* ------------------------------------------------------------------------- */
+
+/* Description */
+
+struct af_info af_info_ladspa = {
+ "LADSPA plugin loader",
+ "ladspa",
+ "Ivo van Poorten",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* By lack of a better word (in my vocabulary) this is called 'parse'.
+ * Feel free to suggest an alternative.
+ */
+
+/** \brief Check for inputs, outputs and controls of a given filter.
+ *
+ * This function counts and checks all input, output and control ports
+ * of the filter that was loaded. If it turns out to be a valid
+ * filter for MPlayer use, it prints out a list of all controls and
+ * the corresponding range of its value at message level MSGL_V.
+ *
+ * \param setup Current setup of the filter. Must have its
+ * plugin_descriptor set!
+ *
+ * \return Returns AF_OK if it has a valid input/output/controls
+ * configuration. Else, it returns AF_ERROR.
+ */
+
+static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
+ int p, i;
+ const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
+ LADSPA_PortDescriptor d;
+ LADSPA_PortRangeHint hint;
+
+ if (!setup->libhandle)
+ return AF_ERROR; /* only call parse after a succesful load */
+ if (!setup->plugin_descriptor)
+ return AF_ERROR; /* same as above */
+
+ /* let's do it */
+
+ setup->nports = pdes->PortCount;
+
+ /* allocate memory for all inputs/outputs/controls */
+
+ setup->inputs = calloc(setup->nports, sizeof(int));
+ if (!setup->inputs) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->outputs = calloc(setup->nports, sizeof(int));
+ if (!setup->outputs) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->inputcontrolsmap = calloc(setup->nports, sizeof(int));
+ if (!setup->inputcontrolsmap) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->inputcontrols = calloc(setup->nports, sizeof(float));
+ if (!setup->inputcontrols) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->outputcontrolsmap = calloc(setup->nports, sizeof(int));
+ if (!setup->outputcontrolsmap) return af_ladspa_malloc_failed(setup->myname);
+
+ setup->outputcontrols = calloc(setup->nports, sizeof(float));
+ if (!setup->outputcontrols) return af_ladspa_malloc_failed(setup->myname);
+
+ /* set counts to zero */
+
+ setup->ninputs = 0;
+ setup->noutputs = 0;
+ setup->ninputcontrols = 0;
+ setup->noutputcontrols = 0;
+
+ /* check all ports, see what type it is and set variables according to
+ * what we have found
+ */
+
+ for (p=0; p<setup->nports; p++) {
+ d = pdes->PortDescriptors[p];
+
+ if (LADSPA_IS_PORT_AUDIO(d)) {
+ if (LADSPA_IS_PORT_INPUT(d)) {
+ setup->inputs[setup->ninputs] = p;
+ setup->ninputs++;
+ } else if (LADSPA_IS_PORT_OUTPUT(d)) {
+ setup->outputs[setup->noutputs] = p;
+ setup->noutputs++;
+ }
+ }
+
+ if (LADSPA_IS_PORT_CONTROL(d)) {
+ if (LADSPA_IS_PORT_INPUT(d)) {
+ setup->inputcontrolsmap[setup->ninputcontrols] = p;
+ setup->ninputcontrols++;
+ /* set control to zero. set values after reading the rest
+ * of the suboptions and check LADSPA_?_HINT's later.
+ */
+ setup->inputcontrols[p] = 0.0f;
+ } else if (LADSPA_IS_PORT_OUTPUT(d)) {
+ /* read and handle these too, otherwise filters that have them
+ * will sig11
+ */
+ setup->outputcontrolsmap[setup->noutputcontrols]=p;
+ setup->noutputcontrols++;
+ setup->outputcontrols[p] = 0.0f;
+ }
+ }
+
+ }
+
+ if (setup->ninputs == 0) {
+ mp_msg(MSGT_AFILTER, MSGL_WARN, "%s: %s\n", setup->myname,
+ _("WARNING! This LADSPA plugin has no audio inputs.\n The incoming audio signal will be lost."));
+ } else if (setup->ninputs == 1) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a mono effect\n", setup->myname);
+ } else if (setup->ninputs == 2) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a stereo effect\n", setup->myname);
+ } else {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a %i-channel effect, "
+ "support is experimental\n", setup->myname, setup->ninputs);
+ }
+
+ if (setup->noutputs == 0) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("This LADSPA plugin has no audio outputs."));
+ return AF_ERROR;
+ }
+
+ if (setup->noutputs != setup->ninputs ) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("The number of audio inputs and audio outputs of the LADSPA plugin differ."));
+ return AF_ERROR;
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: this plugin has %d input control(s)\n",
+ setup->myname, setup->ninputcontrols);
+
+ /* Print list of controls and its range of values it accepts */
+
+ for (i=0; i<setup->ninputcontrols; i++) {
+ p = setup->inputcontrolsmap[i];
+ hint = pdes->PortRangeHints[p];
+ mp_msg(MSGT_AFILTER, MSGL_V, " --- %d %s [", i, pdes->PortNames[p]);
+
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f , ", hint.LowerBound);
+ } else {
+ mp_msg(MSGT_AFILTER, MSGL_V, "... , ");
+ }
+
+ if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f]\n", hint.UpperBound);
+ } else {
+ mp_msg(MSGT_AFILTER, MSGL_V, "...]\n");
+ }
+
+ }
+
+ return AF_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* This function might "slightly" look like dlopenLADSPA in the LADSPA SDK :-)
+ * But, I changed a few things, because imho it was broken. It did not support
+ * relative paths, only absolute paths that start with a /
+ * I think ../../some/dir/foobar.so is just as valid. And if one wants to call
+ * his library '...somename...so' he's crazy, but it should be allowed.
+ * So, search the path first, try plain *filename later.
+ * Also, try adding .so first! I like the recursion the SDK did, but it's
+ * better the other way around. -af ladspa=cmt:amp_stereo:0.5 is easier to type
+ * than -af ladspa=cmt.so:amp_stereo:0.5 :-))
+ */
+
+/** \brief dlopen() wrapper
+ *
+ * This is a wrapper around dlopen(). It tries various variations of the
+ * filename (with or without the addition of the .so extension) in various
+ * directories specified by the LADSPA_PATH environment variable. If all fails
+ * it tries the filename directly as an absolute path to the library.
+ *
+ * \param filename filename of the library to load.
+ * \param flag see dlopen(3) for a description of the flags.
+ *
+ * \return returns a pointer to the loaded library on success, or
+ * NULL if it fails to load.
+ */
+
+static void* mydlopen(const char *filename, int flag) {
+ char *buf;
+ const char *end, *start, *ladspapath;
+ int endsinso, needslash;
+ size_t filenamelen;
+ void *result = NULL;
+
+#if defined(__MINGW32__) || defined(__CYGWIN__)
+ /* For Windows there's only absolute path support.
+ * If you have a Windows machine, feel free to fix this.
+ * (path separator, shared objects extension, et cetera). */
+ mp_msg(MSGT_AFILTER, MSGL_V, "\ton windows, only absolute pathnames "
+ "are supported\n");
+ mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", filename);
+ return dlopen(filename, flag);
+#endif
+
+ filenamelen = strlen(filename);
+
+ endsinso = 0;
+ if (filenamelen > 3)
+ endsinso = (strcmp(filename+filenamelen-3, ".so") == 0);
+ if (!endsinso) {
+ buf=malloc(filenamelen+4);
+ strcpy(buf, filename);
+ strcat(buf, ".so");
+ result=mydlopen(buf, flag);
+ free(buf);
+ }
+
+ if (result)
+ return result;
+
+ ladspapath=getenv("LADSPA_PATH");
+
+ if (ladspapath) {
+
+ start=ladspapath;
+ while (*start != '\0') {
+ end=start;
+ while ( (*end != ':') && (*end != '\0') )
+ end++;
+
+ buf=malloc(filenamelen + 2 + (end-start) );
+ if (end > start)
+ strncpy(buf, start, end-start);
+ needslash=0;
+ if (end > start)
+ if (*(end-1) != '/') {
+ needslash = 1;
+ buf[end-start] = '/';
+ }
+ strcpy(buf+needslash+(end-start), filename);
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", buf);
+ result=dlopen(buf, flag);
+
+ free(buf);
+ if (result)
+ return result;
+
+ start = end;
+ if (*start == ':')
+ start++;
+ } /* end while there's still more in the path */
+ } /* end if there's a ladspapath */
+
+ /* last resort, just open it again, so the dlerror() message is correct */
+ mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", filename);
+ return dlopen(filename,flag);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Load a LADSPA Plugin
+ *
+ * This function loads the LADSPA plugin specified by the file and label
+ * that are present in the setup variable. First, it loads the library.
+ * If it fails, it returns AF_ERROR. If not, it continues to look for the
+ * specified label. If it finds it, it sets the plugin_descriptor inside
+ * setup and returns AF_OK. If it doesn't, it returns AF_ERROR. Special case
+ * is a label called 'help'. In that case, it prints a list of all available
+ * labels (filters) in the library specified by file.
+ *
+ * \param setup Current setup of the filter. Contains filename and label.
+ *
+ * \return Either AF_ERROR or AF_OK, depending on the success of the operation.
+ */
+
+static int af_ladspa_load_plugin(af_ladspa_t *setup) {
+ const LADSPA_Descriptor *ladspa_descriptor;
+ LADSPA_Descriptor_Function descriptor_function;
+ int i;
+
+ /* load library */
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: loading ladspa plugin library %s\n",
+ setup->myname, setup->file);
+
+ setup->libhandle = mydlopen(setup->file, RTLD_NOW);
+
+ if (!setup->libhandle) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s %s\n\t%s\n", setup->myname,
+ _("failed to load"), setup->file, dlerror() );
+ return AF_ERROR;
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: library found.\n", setup->myname);
+
+ /* find descriptor function */
+ dlerror();
+ descriptor_function = (LADSPA_Descriptor_Function) dlsym (setup->libhandle,
+ "ladspa_descriptor");
+
+ if (!descriptor_function) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n\t%s\n", setup->myname,
+ _("Couldn't find ladspa_descriptor() function in the specified library file."), dlerror());
+ return AF_ERROR;
+ }
+
+ /* if label == help, list all labels in library and exit */
+
+ if (strcmp(setup->label, "help") == 0) {
+ mp_msg(MSGT_AFILTER, MSGL_INFO, "%s: %s %s:\n", setup->myname,
+ _("available labels in"), setup->file);
+ for (i=0; ; i++) {
+ ladspa_descriptor = descriptor_function(i);
+ if (ladspa_descriptor == NULL) {
+ return AF_ERROR;
+ }
+ mp_msg(MSGT_AFILTER, MSGL_INFO, " %-16s - %s (%lu)\n",
+ ladspa_descriptor->Label,
+ ladspa_descriptor->Name,
+ ladspa_descriptor->UniqueID);
+ }
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: looking for label\n", setup->myname);
+
+ /* find label in library */
+ for (i=0; ; i++) {
+ ladspa_descriptor = descriptor_function(i);
+ if (ladspa_descriptor == NULL) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("Couldn't find label in plugin library."));
+ return AF_ERROR;
+ }
+ if (strcmp(ladspa_descriptor->Label, setup->label) == 0) {
+ setup->plugin_descriptor = ladspa_descriptor;
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: %s found\n", setup->myname,
+ setup->label);
+ return AF_OK;
+ }
+ }
+
+ return AF_OK;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Print a malloc() failed error message.
+ *
+ * Generic function which can be called if a call to malloc(), calloc(),
+ * strdup(), et cetera, failed. It prints a message to the console and
+ * returns AF_ERROR.
+ *
+ * \return AF_ERROR
+ */
+
+static int af_ladspa_malloc_failed(char *myname) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s", myname, "Memory allocation failed.\n");
+ return AF_ERROR;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Controls the filter.
+ *
+ * Control the behaviour of the filter.
+ *
+ * Commands:
+ * CONTROL_REINIT Sets the af structure with proper values for number
+ * of channels, rate, format, et cetera.
+ * CONTROL_COMMAND_LINE Parses the suboptions given to this filter
+ * through arg. It first parses the filename and
+ * the label. After that, it loads the filter
+ * and finds out its proprties. Then in continues
+ * parsing the controls given on the commandline,
+ * if any are needed.
+ *
+ * \param af Audio filter instance
+ * \param cmd The command to execute
+ * \param arg Arguments to the command
+ *
+ * \return Either AF_ERROR or AF_OK, depending on the succes of the
+ * operation.
+ */
+
+static int control(struct af_instance *af, int cmd, void *arg) {
+ af_ladspa_t *setup = (af_ladspa_t*) af->setup;
+ int i, r;
+ float val;
+
+ switch(cmd) {
+ case AF_CONTROL_REINIT:
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: (re)init\n", setup->myname);
+
+ if (!arg) return AF_ERROR;
+
+ /* accept FLOAT, let af_format do conversion */
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+
+ /* arg->len is not set here yet, so init of buffers and connecting the
+ * filter, has to be done in play() :-/
+ */
+
+ return af_test_output(af, (struct mp_audio*)arg);
+ case AF_CONTROL_COMMAND_LINE: {
+ char *buf;
+ char *line = arg;
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: parse suboptions\n", setup->myname);
+
+ /* suboption parser here!
+ * format is (ladspa=)file:label:controls....
+ */
+
+ if (!line) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("No suboptions specified."));
+ return AF_ERROR;
+ }
+
+ buf = malloc(strlen(line)+1);
+ if (!buf) return af_ladspa_malloc_failed(setup->myname);
+
+ /* file... */
+ buf[0] = '\0';
+ sscanf(line, "%[^:]", buf);
+ if (buf[0] == '\0') {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("No library file specified."));
+ free(buf);
+ return AF_ERROR;
+ }
+ line += strlen(buf);
+ setup->file = strdup(buf);
+ if (!setup->file) return af_ladspa_malloc_failed(setup->myname);
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: file --> %s\n", setup->myname,
+ setup->file);
+ if (*line != '\0') line++; /* read ':' */
+
+ /* label... */
+ buf[0] = '\0';
+ sscanf(line, "%[^:]", buf);
+ if (buf[0] == '\0') {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("No filter label specified."));
+ free(buf);
+ return AF_ERROR;
+ }
+ line += strlen(buf);
+ setup->label = strdup(buf);
+ if (!setup->label) return af_ladspa_malloc_failed(setup->myname);
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: label --> %s\n", setup->myname,
+ setup->label);
+/* if (*line != '0') line++; */ /* read ':' */
+
+ free(buf); /* no longer needed */
+
+ /* set new setup->myname */
+
+ free(setup->myname);
+ setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+
+ strlen(setup->label)+6, 1);
+ snprintf(setup->myname, strlen(af_info_ladspa.name)+
+ strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)",
+ af_info_ladspa.name, setup->file, setup->label);
+
+ /* load plugin :) */
+
+ if ( af_ladspa_load_plugin(setup) != AF_OK )
+ return AF_ERROR;
+
+ /* see what inputs, outputs and controls this plugin has */
+ if ( af_ladspa_parse_plugin(setup) != AF_OK )
+ return AF_ERROR;
+
+ /* ninputcontrols is set by now, read control values from arg */
+
+ for(i=0; i<setup->ninputcontrols; i++) {
+ if (!line || *line != ':') {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("Not enough controls specified on the command line."));
+ return AF_ERROR;
+ }
+ line++;
+ r = sscanf(line, "%f", &val);
+ if (r!=1) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
+ _("Not enough controls specified on the command line."));
+ return AF_ERROR;
+ }
+ setup->inputcontrols[setup->inputcontrolsmap[i]] = val;
+ line = strchr(line, ':');
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: input controls: ", setup->myname);
+ for(i=0; i<setup->ninputcontrols; i++) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%0.4f ",
+ setup->inputcontrols[setup->inputcontrolsmap[i]]);
+ }
+ mp_msg(MSGT_AFILTER, MSGL_V, "\n");
+
+ /* check boundaries of inputcontrols */
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: checking boundaries of input controls\n",
+ setup->myname);
+ for(i=0; i<setup->ninputcontrols; i++) {
+ int p = setup->inputcontrolsmap[i];
+ LADSPA_PortRangeHint hint =
+ setup->plugin_descriptor->PortRangeHints[p];
+ val = setup->inputcontrols[p];
+
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
+ val < hint.LowerBound) {
+ mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is below lower boundary of %0.4f.\n",
+ setup->myname, i, hint.LowerBound);
+ return AF_ERROR;
+ }
+ if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
+ val > hint.UpperBound) {
+ mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is above upper boundary of %0.4f.\n",
+ setup->myname, i, hint.UpperBound);
+ return AF_ERROR;
+ }
+ }
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: all controls have sane values\n",
+ setup->myname);
+
+ /* All is well! */
+ setup->status = AF_OK;
+
+ return AF_OK; }
+ }
+
+ return AF_UNKNOWN;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Uninitialise the LADSPA Plugin Loader filter.
+ *
+ * This function deactivates the plugin(s), cleans up, frees all allocated
+ * memory and exits.
+ *
+ * \return No return value.
+ */
+
+static void uninit(struct af_instance *af) {
+ int i;
+
+ free(af->data);
+ if (af->setup) {
+ af_ladspa_t *setup = (af_ladspa_t*) af->setup;
+ const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
+
+ if (setup->myname) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "%s: cleaning up\n", setup->myname);
+ free(setup->myname);
+ }
+
+ if (setup->chhandles) {
+ for(i=0; i<setup->nch; i+=setup->ninputs) {
+ if (pdes->deactivate) pdes->deactivate(setup->chhandles[i]);
+ if (pdes->cleanup) pdes->cleanup(setup->chhandles[i]);
+ }
+ free(setup->chhandles);
+ }
+
+ free(setup->file);
+ free(setup->label);
+ free(setup->inputcontrolsmap);
+ free(setup->inputcontrols);
+ free(setup->outputcontrolsmap);
+ free(setup->outputcontrols);
+ free(setup->inputs);
+ free(setup->outputs);
+
+ if (setup->inbufs) {
+ for(i=0; i<setup->nch; i++)
+ free(setup->inbufs[i]);
+ free(setup->inbufs);
+ }
+
+ if (setup->outbufs) {
+ for(i=0; i<setup->nch; i++)
+ free(setup->outbufs[i]);
+ free(setup->outbufs);
+ }
+
+ if (setup->libhandle)
+ dlclose(setup->libhandle);
+
+ free(setup);
+ setup = NULL;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Process chunk of audio data through the selected LADSPA Plugin.
+ *
+ * \param af Pointer to audio filter instance
+ * \param data Pointer to chunk of audio data
+ *
+ * \return Either AF_ERROR or AF_OK
+ */
+
+static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) {
+ af_ladspa_t *setup = af->setup;
+ const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
+ float *audio = (float*)data->audio;
+ int nsamples = data->len/4; /* /4 because it's 32-bit float */
+ int nch = data->nch;
+ int rate = data->rate;
+ int i, p;
+
+ if (setup->status !=AF_OK)
+ return data;
+
+ /* See if it's the first call. If so, setup inbufs/outbufs, instantiate
+ * plugin, connect ports and activate plugin
+ */
+
+ /* 2004-12-07: Also check if the buffersize has to be changed!
+ * data->len is not constant per se! re-init buffers.
+ */
+
+ if ( (setup->bufsize != nsamples/nch) || (setup->nch != nch) ) {
+
+ /* if setup->nch==0, it's the first call, if not, something has
+ * changed and all previous mallocs have to be freed
+ */
+
+ if (setup->nch != 0) {
+ mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize change; free old buffer\n",
+ setup->myname);
+
+ if(setup->inbufs) {
+ for(i=0; i<setup->nch; i++)
+ free(setup->inbufs[i]);
+ free(setup->inbufs);
+ }
+ if(setup->outbufs) {
+ for(i=0; i<setup->nch; i++)
+ free(setup->outbufs[i]);
+ free(setup->outbufs);
+ }
+ } /* everything is freed */
+
+ setup->bufsize = nsamples/nch;
+ setup->nch = nch;
+
+ setup->inbufs = calloc(nch, sizeof(float*));
+ setup->outbufs = calloc(nch, sizeof(float*));
+
+ mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize = %d\n",
+ setup->myname, setup->bufsize);
+
+ for(i=0; i<nch; i++) {
+ setup->inbufs[i] = calloc(setup->bufsize, sizeof(float));
+ setup->outbufs[i] = calloc(setup->bufsize, sizeof(float));
+ }
+
+ /* only on the first call, there are no handles. */
+
+ if (!setup->chhandles) {
+ setup->chhandles = calloc(nch, sizeof(LADSPA_Handle));
+
+ /* create handles
+ * for stereo effects, create one handle for two channels
+ */
+
+ for(i=0; i<nch; i++) {
+
+ if (i % setup->ninputs) { /* stereo effect */
+ /* copy the handle from previous channel */
+ setup->chhandles[i] = setup->chhandles[i-1];
+ continue;
+ }
+
+ setup->chhandles[i] = pdes->instantiate(pdes, rate);
+ }
+ }
+
+ /* connect input/output ports for each channel/filter instance
+ *
+ * always (re)connect ports
+ */
+
+ for(i=0; i<nch; i++) {
+ pdes->connect_port(setup->chhandles[i],
+ setup->inputs[i % setup->ninputs],
+ setup->inbufs[i]);
+ pdes->connect_port(setup->chhandles[i],
+ setup->outputs[i % setup->ninputs],
+ setup->outbufs[i]);
+
+ /* connect (input) controls */
+
+ for (p=0; p<setup->nports; p++) {
+ LADSPA_PortDescriptor d = pdes->PortDescriptors[p];
+ if (LADSPA_IS_PORT_CONTROL(d)) {
+ if (LADSPA_IS_PORT_INPUT(d)) {
+ pdes->connect_port(setup->chhandles[i], p,
+ &(setup->inputcontrols[p]) );
+ } else {
+ pdes->connect_port(setup->chhandles[i], p,
+ &(setup->outputcontrols[p]) );
+ }
+ }
+ }
+
+ /* Activate filter (if it isn't already :) ) */
+
+ if (pdes->activate && !setup->activated && i % setup->ninputs == 0)
+ pdes->activate(setup->chhandles[i]);
+
+ } /* All channels/filters done! except for... */
+ setup->activated = 1;
+
+ /* Stereo effect with one channel left. Use same buffer for left
+ * and right. connect it to the second port.
+ */
+
+ for (p = i; p % setup->ninputs; p++) {
+ pdes->connect_port(setup->chhandles[i-1],
+ setup->inputs[p % setup->ninputs],
+ setup->inbufs[i-1]);
+ pdes->connect_port(setup->chhandles[i-1],
+ setup->outputs[p % setup->ninputs],
+ setup->outbufs[i-1]);
+ } /* done! */
+
+ } /* setup for first call/change of bufsize is done.
+ * normal playing routine follows...
+ */
+
+ /* Right now, I use a separate input and output buffer.
+ * I could change this to in-place processing (inbuf==outbuf), but some
+ * ladspa filters are broken and are not able to handle that. This seems
+ * fast enough, so unless somebody complains, it stays this way :)
+ */
+
+ /* Fill inbufs */
+
+ for (p=0; p<setup->bufsize; p++) {
+ for (i=0; i<nch; i++) {
+ setup->inbufs[i][p] = audio[p*nch + i];
+ }
+ }
+
+ /* Run filter(s) */
+
+ for (i=0; i<nch; i+=setup->ninputs) {
+ pdes->run(setup->chhandles[i], setup->bufsize);
+ }
+
+ /* Extract outbufs */
+
+ for (p=0; p<setup->bufsize; p++) {
+ for (i=0; i<nch; i++) {
+ audio[p*nch + i] = setup->outbufs[i][p];
+ }
+ }
+
+ /* done */
+
+ return data;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/** \brief Open LADSPA Plugin Loader Filter
+ *
+ * \param af Audio Filter instance
+ *
+ * \return Either AF_ERROR or AF_OK
+ */
+
+static int af_open(struct af_instance *af) {
+
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+
+ af->data = calloc(1, sizeof(struct mp_audio));
+ if (af->data == NULL)
+ return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
+
+ af->setup = calloc(1, sizeof(af_ladspa_t));
+ if (af->setup == NULL) {
+ free(af->data);
+ af->data=NULL;
+ return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
+ }
+
+ ((af_ladspa_t*)af->setup)->status = AF_ERROR; /* will be set to AF_OK if
+ * all went OK and play()
+ * should proceed.
+ */
+
+ ((af_ladspa_t*)af->setup)->myname = strdup(af_info_ladspa.name);
+ if (!((af_ladspa_t*)af->setup)->myname)
+ return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
+
+ return AF_OK;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/audio/filter/af_lavcac3enc.c b/audio/filter/af_lavcac3enc.c
new file mode 100644
index 0000000000..ad78266ad3
--- /dev/null
+++ b/audio/filter/af_lavcac3enc.c
@@ -0,0 +1,332 @@
+/*
+ * audio filter for runtime AC-3 encoding with libavcodec.
+ *
+ * Copyright (C) 2007 Ulion <ulion A gmail P com>
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <libavcodec/avcodec.h>
+#include <libavutil/intreadwrite.h>
+#include <libavutil/mem.h>
+
+#include "config.h"
+#include "af.h"
+#include "reorder_ch.h"
+
+
+#define AC3_MAX_CHANNELS 6
+#define AC3_MAX_CODED_FRAME_SIZE 3840
+#define AC3_FRAME_SIZE (6 * 256)
+const uint16_t ac3_bitrate_tab[19] = {
+ 32, 40, 48, 56, 64, 80, 96, 112, 128,
+ 160, 192, 224, 256, 320, 384, 448, 512, 576, 640
+};
+
+// Data for specific instances of this filter
+typedef struct af_ac3enc_s {
+ struct AVCodec *lavc_acodec;
+ struct AVCodecContext *lavc_actx;
+ int add_iec61937_header;
+ int bit_rate;
+ int pending_data_size;
+ char *pending_data;
+ int pending_len;
+ int expect_len;
+ int min_channel_num;
+ int in_sampleformat;
+} af_ac3enc_t;
+
+// Initialization and runtime control
+static int control(struct af_instance *af, int cmd, void *arg)
+{
+ af_ac3enc_t *s = af->setup;
+ struct mp_audio *data = arg;
+ int i, bit_rate, test_output_res;
+ static const int default_bit_rate[AC3_MAX_CHANNELS+1] = \
+ {0, 96000, 192000, 256000, 384000, 448000, 448000};
+
+ switch (cmd){
+ case AF_CONTROL_REINIT:
+ if (AF_FORMAT_IS_AC3(data->format) || data->nch < s->min_channel_num)
+ return AF_DETACH;
+
+ af->data->format = s->in_sampleformat;
+ af->data->bps = af_fmt2bits(s->in_sampleformat) / 8;
+ if (data->rate == 48000 || data->rate == 44100 || data->rate == 32000)
+ af->data->rate = data->rate;
+ else
+ af->data->rate = 48000;
+ if (data->nch > AC3_MAX_CHANNELS)
+ af->data->nch = AC3_MAX_CHANNELS;
+ else
+ af->data->nch = data->nch;
+ test_output_res = af_test_output(af, data);
+
+ s->pending_len = 0;
+ s->expect_len = AC3_FRAME_SIZE * data->nch * af->data->bps;
+ assert(s->expect_len <= s->pending_data_size);
+ if (s->add_iec61937_header)
+ af->mul = (double)AC3_FRAME_SIZE * 2 * 2 / s->expect_len;
+ else
+ af->mul = (double)AC3_MAX_CODED_FRAME_SIZE / s->expect_len;
+
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc reinit: %d, %d, %f, %d.\n",
+ data->nch, data->rate, af->mul, s->expect_len);
+
+ bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[af->data->nch];
+
+ if (s->lavc_actx->channels != af->data->nch ||
+ s->lavc_actx->sample_rate != af->data->rate ||
+ s->lavc_actx->bit_rate != bit_rate) {
+
+ avcodec_close(s->lavc_actx);
+
+ // Put sample parameters
+ s->lavc_actx->channels = af->data->nch;
+ s->lavc_actx->sample_rate = af->data->rate;
+ s->lavc_actx->bit_rate = bit_rate;
+
+ if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) {
+ mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate);
+ return AF_ERROR;
+ }
+ }
+ if (s->lavc_actx->frame_size != AC3_FRAME_SIZE) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "lavcac3enc: unexpected ac3 "
+ "encoder frame size %d\n", s->lavc_actx->frame_size);
+ return AF_ERROR;
+ }
+ af->data->format = AF_FORMAT_AC3_BE;
+ af->data->bps = 2;
+ af->data->nch = 2;
+ return test_output_res;
+ case AF_CONTROL_COMMAND_LINE:
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc cmdline: %s.\n", (char*)arg);
+ s->bit_rate = 0;
+ s->min_channel_num = 0;
+ s->add_iec61937_header = 0;
+ sscanf(arg,"%d:%d:%d", &s->add_iec61937_header, &s->bit_rate,
+ &s->min_channel_num);
+ if (s->bit_rate < 1000)
+ s->bit_rate *= 1000;
+ if (s->bit_rate) {
+ for (i = 0; i < 19; ++i)
+ if (ac3_bitrate_tab[i] * 1000 == s->bit_rate)
+ break;
+ if (i >= 19) {
+ mp_msg(MSGT_AFILTER, MSGL_WARN, "af_lavcac3enc unable set unsupported "
+ "bitrate %d, use default bitrate (check manpage to see "
+ "supported bitrates).\n", s->bit_rate);
+ s->bit_rate = 0;
+ }
+ }
+ if (s->min_channel_num == 0)
+ s->min_channel_num = 5;
+ mp_msg(MSGT_AFILTER, MSGL_V, "af_lavcac3enc config spdif:%d, bitrate:%d, "
+ "minchnum:%d.\n", s->add_iec61937_header, s->bit_rate,
+ s->min_channel_num);
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ if (af->data)
+ free(af->data->audio);
+ free(af->data);
+ if (af->setup) {
+ af_ac3enc_t *s = af->setup;
+ af->setup = NULL;
+ if(s->lavc_actx) {
+ avcodec_close(s->lavc_actx);
+ av_free(s->lavc_actx);
+ }
+ free(s->pending_data);
+ free(s);
+ }
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ af_ac3enc_t *s = af->setup;
+ struct mp_audio *c = data; // Current working data
+ struct mp_audio *l;
+ int len, left, outsize = 0, destsize;
+ char *buf, *src, *dest;
+ int max_output_len;
+ int frame_num = (data->len + s->pending_len) / s->expect_len;
+ int samplesize = af_fmt2bits(s->in_sampleformat) / 8;
+
+ if (s->add_iec61937_header)
+ max_output_len = AC3_FRAME_SIZE * 2 * 2 * frame_num;
+ else
+ max_output_len = AC3_MAX_CODED_FRAME_SIZE * frame_num;
+
+ if (af->data->len < max_output_len) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
+ "old len = %i, new len = %i\n", af->info->name, af->data->len,
+ max_output_len);
+ free(af->data->audio);
+ af->data->audio = malloc(max_output_len);
+ if (!af->data->audio) {
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
+ return NULL;
+ }
+ af->data->len = max_output_len;
+ }
+
+ l = af->data; // Local data
+ buf = l->audio;
+ src = c->audio;
+ left = c->len;
+
+
+ while (left > 0) {
+ if (left + s->pending_len < s->expect_len) {
+ memcpy(s->pending_data + s->pending_len, src, left);
+ src += left;
+ s->pending_len += left;
+ left = 0;
+ break;
+ }
+
+ dest = s->add_iec61937_header ? buf + 8 : buf;
+ destsize = (char *)l->audio + l->len - buf;
+
+ if (s->pending_len) {
+ int needs = s->expect_len - s->pending_len;
+ if (needs > 0) {
+ memcpy(s->pending_data + s->pending_len, src, needs);
+ src += needs;
+ left -= needs;
+ }
+
+ if (c->nch >= 5)
+ reorder_channel_nch(s->pending_data,
+ AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
+ AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
+ c->nch,
+ s->expect_len / samplesize, samplesize);
+
+ len = avcodec_encode_audio(s->lavc_actx, dest, destsize,
+ (void *)s->pending_data);
+ s->pending_len = 0;
+ }
+ else {
+ if (c->nch >= 5)
+ reorder_channel_nch(src,
+ AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
+ AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
+ c->nch,
+ s->expect_len / samplesize, samplesize);
+ len = avcodec_encode_audio(s->lavc_actx,dest,destsize,(void *)src);
+ src += s->expect_len;
+ left -= s->expect_len;
+ }
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "avcodec_encode_audio got %d, pending %d.\n",
+ len, s->pending_len);
+
+ if (s->add_iec61937_header) {
+ int bsmod = dest[5] & 0x7;
+
+ AV_WB16(buf, 0xF872); // iec 61937 syncword 1
+ AV_WB16(buf + 2, 0x4E1F); // iec 61937 syncword 2
+ buf[4] = bsmod; // bsmod
+ buf[5] = 0x01; // data-type ac3
+ AV_WB16(buf + 6, len << 3); // number of bits in payload
+
+ memset(buf + 8 + len, 0, AC3_FRAME_SIZE * 2 * 2 - 8 - len);
+ len = AC3_FRAME_SIZE * 2 * 2;
+ }
+
+ outsize += len;
+ buf += len;
+ }
+ c->audio = l->audio;
+ c->nch = 2;
+ c->bps = 2;
+ c->len = outsize;
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "play return size %d, pending %d\n",
+ outsize, s->pending_len);
+ return c;
+}
+
+static int af_open(struct af_instance* af){
+
+ af_ac3enc_t *s = calloc(1,sizeof(af_ac3enc_t));
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=s;
+
+ s->lavc_acodec = avcodec_find_encoder_by_name("ac3");
+ if (!s->lavc_acodec) {
+ mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, couldn't find encoder for codec %s.\n", "ac3");
+ return AF_ERROR;
+ }
+
+ s->lavc_actx = avcodec_alloc_context3(s->lavc_acodec);
+ if (!s->lavc_actx) {
+ mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, couldn't allocate context!\n");
+ return AF_ERROR;
+ }
+ const enum AVSampleFormat *fmts = s->lavc_acodec->sample_fmts;
+ for (int i = 0; ; i++) {
+ if (fmts[i] == AV_SAMPLE_FMT_NONE) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, encoder doesn't "
+ "support expected sample formats!\n");
+ return AF_ERROR;
+ } else if (fmts[i] == AV_SAMPLE_FMT_S16) {
+ s->in_sampleformat = AF_FORMAT_S16_NE;
+ s->lavc_actx->sample_fmt = fmts[i];
+ break;
+ } else if (fmts[i] == AV_SAMPLE_FMT_FLT) {
+ s->in_sampleformat = AF_FORMAT_FLOAT_NE;
+ s->lavc_actx->sample_fmt = fmts[i];
+ break;
+ }
+ }
+ char buf[100];
+ mp_msg(MSGT_AFILTER, MSGL_V, "[af_lavcac3enc]: in sample format: %s\n",
+ af_fmt2str(s->in_sampleformat, buf, 100));
+ s->pending_data_size = AF_NCH * AC3_FRAME_SIZE *
+ af_fmt2bits(s->in_sampleformat) / 8;
+ s->pending_data = malloc(s->pending_data_size);
+
+ return AF_OK;
+}
+
+struct af_info af_info_lavcac3enc = {
+ "runtime encode to ac3 using libavcodec",
+ "lavcac3enc",
+ "Ulion",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_lavcresample.c b/audio/filter/af_lavcresample.c
new file mode 100644
index 0000000000..ce777fed31
--- /dev/null
+++ b/audio/filter/af_lavcresample.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.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 <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "af.h"
+#include "libavcodec/avcodec.h"
+#include "libavutil/rational.h"
+
+// Data for specific instances of this filter
+typedef struct af_resample_s{
+ struct AVResampleContext *avrctx;
+ int16_t *in[AF_NCH];
+ int in_alloc;
+ int index;
+
+ int filter_length;
+ int linear;
+ int phase_shift;
+ double cutoff;
+
+ int ctx_out_rate;
+ int ctx_in_rate;
+ int ctx_filter_size;
+ int ctx_phase_shift;
+ int ctx_linear;
+ double ctx_cutoff;
+}af_resample_t;
+
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_resample_t* s = (af_resample_t*)af->setup;
+ struct mp_audio *data= (struct mp_audio*)arg;
+ int out_rate, test_output_res; // helpers for checking input format
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ if((af->data->rate == data->rate) || (af->data->rate == 0))
+ return AF_DETACH;
+
+ af->data->nch = data->nch;
+ if (af->data->nch > AF_NCH) af->data->nch = AF_NCH;
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ af->mul = (double)af->data->rate / data->rate;
+ af->delay = af->data->nch * s->filter_length / min(af->mul, 1); // *bps*.5
+
+ if (s->ctx_out_rate != af->data->rate || s->ctx_in_rate != data->rate || s->ctx_filter_size != s->filter_length ||
+ s->ctx_phase_shift != s->phase_shift || s->ctx_linear != s->linear || s->ctx_cutoff != s->cutoff) {
+ if(s->avrctx) av_resample_close(s->avrctx);
+ s->avrctx= av_resample_init(af->data->rate, /*in_rate*/data->rate, s->filter_length, s->phase_shift, s->linear, s->cutoff);
+ s->ctx_out_rate = af->data->rate;
+ s->ctx_in_rate = data->rate;
+ s->ctx_filter_size = s->filter_length;
+ s->ctx_phase_shift = s->phase_shift;
+ s->ctx_linear = s->linear;
+ s->ctx_cutoff = s->cutoff;
+ }
+
+ // hack to make af_test_output ignore the samplerate change
+ out_rate = af->data->rate;
+ af->data->rate = data->rate;
+ test_output_res = af_test_output(af, (struct mp_audio*)arg);
+ af->data->rate = out_rate;
+ return test_output_res;
+ case AF_CONTROL_COMMAND_LINE:{
+ s->cutoff= 0.0;
+ sscanf((char*)arg,"%d:%d:%d:%d:%lf", &af->data->rate, &s->filter_length, &s->linear, &s->phase_shift, &s->cutoff);
+ if(s->cutoff <= 0.0) s->cutoff= max(1.0 - 6.5/(s->filter_length+8), 0.80);
+ return AF_OK;
+ }
+ case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET:
+ af->data->rate = *(int*)arg;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ if(af->data)
+ free(af->data->audio);
+ free(af->data);
+ if(af->setup){
+ int i;
+ af_resample_t *s = af->setup;
+ if(s->avrctx) av_resample_close(s->avrctx);
+ for (i=0; i < AF_NCH; i++)
+ free(s->in[i]);
+ free(s);
+ }
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ af_resample_t *s = af->setup;
+ int i, j, consumed, ret = 0;
+ int16_t *in = (int16_t*)data->audio;
+ int16_t *out;
+ int chans = data->nch;
+ int in_len = data->len/(2*chans);
+ int out_len = in_len * af->mul + 10;
+ int16_t tmp[AF_NCH][out_len];
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ out= (int16_t*)af->data->audio;
+
+ out_len= min(out_len, af->data->len/(2*chans));
+
+ if(s->in_alloc < in_len + s->index){
+ s->in_alloc= in_len + s->index;
+ for(i=0; i<chans; i++){
+ s->in[i]= realloc(s->in[i], s->in_alloc*sizeof(int16_t));
+ }
+ }
+
+ if(chans==1){
+ memcpy(&s->in[0][s->index], in, in_len * sizeof(int16_t));
+ }else if(chans==2){
+ for(j=0; j<in_len; j++){
+ s->in[0][j + s->index]= *(in++);
+ s->in[1][j + s->index]= *(in++);
+ }
+ }else{
+ for(j=0; j<in_len; j++){
+ for(i=0; i<chans; i++){
+ s->in[i][j + s->index]= *(in++);
+ }
+ }
+ }
+ in_len += s->index;
+
+ for(i=0; i<chans; i++){
+ ret= av_resample(s->avrctx, tmp[i], s->in[i], &consumed, in_len, out_len, i+1 == chans);
+ }
+ out_len= ret;
+
+ s->index= in_len - consumed;
+ for(i=0; i<chans; i++){
+ memmove(s->in[i], s->in[i] + consumed, s->index*sizeof(int16_t));
+ }
+
+ if(chans==1){
+ memcpy(out, tmp[0], out_len*sizeof(int16_t));
+ }else if(chans==2){
+ for(j=0; j<out_len; j++){
+ *(out++)= tmp[0][j];
+ *(out++)= tmp[1][j];
+ }
+ }else{
+ for(j=0; j<out_len; j++){
+ for(i=0; i<chans; i++){
+ *(out++)= tmp[i][j];
+ }
+ }
+ }
+
+ data->audio = af->data->audio;
+ data->len = out_len*chans*2;
+ data->rate = af->data->rate;
+ return data;
+}
+
+static int af_open(struct af_instance* af){
+ af_resample_t *s = calloc(1,sizeof(af_resample_t));
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ s->filter_length= 16;
+ s->cutoff= max(1.0 - 6.5/(s->filter_length+8), 0.80);
+ s->phase_shift= 10;
+// s->setup = RSMP_INT | FREQ_SLOPPY;
+ af->setup=s;
+ return AF_OK;
+}
+
+struct af_info af_info_lavcresample = {
+ "Sample frequency conversion using libavcodec",
+ "lavcresample",
+ "Michael Niedermayer",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_pan.c b/audio/filter/af_pan.c
new file mode 100644
index 0000000000..8b1783ee84
--- /dev/null
+++ b/audio/filter/af_pan.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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 <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_pan_s
+{
+ int nch; // Number of output channels; zero means same as input
+ float level[AF_NCH][AF_NCH]; // Gain level for each channel
+}af_pan_t;
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_pan_t* s = af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ af->data->nch = s->nch ? s->nch: ((struct mp_audio*)arg)->nch;
+ af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
+
+ if((af->data->format != ((struct mp_audio*)arg)->format) ||
+ (af->data->bps != ((struct mp_audio*)arg)->bps)){
+ ((struct mp_audio*)arg)->format = af->data->format;
+ ((struct mp_audio*)arg)->bps = af->data->bps;
+ return AF_FALSE;
+ }
+ return AF_OK;
+ case AF_CONTROL_COMMAND_LINE:{
+ int nch = 0;
+ int n = 0;
+ char* cp = NULL;
+ int j,k;
+ // Read number of outputs
+ sscanf((char*)arg,"%i%n", &nch,&n);
+ if(AF_OK != control(af,AF_CONTROL_PAN_NOUT | AF_CONTROL_SET, &nch))
+ return AF_ERROR;
+
+ // Read pan values
+ cp = &((char*)arg)[n];
+ j = 0; k = 0;
+ while((*cp == ':') && (k < AF_NCH)){
+ sscanf(cp, ":%f%n" , &s->level[j][k], &n);
+ mp_msg(MSGT_AFILTER, MSGL_V, "[pan] Pan level from channel %i to"
+ " channel %i = %f\n",k,j,s->level[j][k]);
+ cp =&cp[n];
+ j++;
+ if(j>=nch){
+ j = 0;
+ k++;
+ }
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET:{
+ int i;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ float* level = ((af_control_ext_t*)arg)->arg;
+ if (ch >= AF_NCH)
+ return AF_FALSE;
+ for(i=0;i<AF_NCH;i++)
+ s->level[ch][i] = level[i];
+ return AF_OK;
+ }
+ case AF_CONTROL_PAN_LEVEL | AF_CONTROL_GET:{
+ int i;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ float* level = ((af_control_ext_t*)arg)->arg;
+ if (ch >= AF_NCH)
+ return AF_FALSE;
+ for(i=0;i<AF_NCH;i++)
+ level[i] = s->level[ch][i];
+ return AF_OK;
+ }
+ case AF_CONTROL_PAN_NOUT | AF_CONTROL_SET:
+ // Reinit must be called after this function has been called
+
+ // Sanity check
+ if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[pan] The number of output channels must be"
+ " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
+ return AF_ERROR;
+ }
+ s->nch=((int*)arg)[0];
+ return AF_OK;
+ case AF_CONTROL_PAN_NOUT | AF_CONTROL_GET:
+ *(int*)arg = af->data->nch;
+ return AF_OK;
+ case AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET:{
+ float val = *(float*)arg;
+ if (s->nch)
+ return AF_ERROR;
+ if (af->data->nch >= 2) {
+ s->level[0][0] = min(1.f, 1.f - val);
+ s->level[0][1] = max(0.f, val);
+ s->level[1][0] = max(0.f, -val);
+ s->level[1][1] = min(1.f, 1.f + val);
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET:
+ if (s->nch)
+ return AF_ERROR;
+ *(float*)arg = s->level[0][1] - s->level[1][0];
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ if(af->data)
+ free(af->data->audio);
+ free(af->data);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ struct mp_audio* l = af->data; // Local data
+ af_pan_t* s = af->setup; // Setup for this instance
+ float* in = c->audio; // Input audio data
+ float* out = NULL; // Output audio data
+ float* end = in+c->len/4; // End of loop
+ int nchi = c->nch; // Number of input channels
+ int ncho = l->nch; // Number of output channels
+ register int j,k;
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ out = l->audio;
+ // Execute panning
+ // FIXME: Too slow
+ while(in < end){
+ for(j=0;j<ncho;j++){
+ register float x = 0.0;
+ register float* tin = in;
+ for(k=0;k<nchi;k++)
+ x += tin[k] * s->level[j][k];
+ out[j] = x;
+ }
+ out+= ncho;
+ in+= nchi;
+ }
+
+ // Set output data
+ c->audio = l->audio;
+ c->len = c->len / c->nch * l->nch;
+ c->nch = l->nch;
+
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_pan_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_pan = {
+ "Panning audio filter",
+ "pan",
+ "Anders",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_resample.c b/audio/filter/af_resample.c
new file mode 100644
index 0000000000..1f0b7cc942
--- /dev/null
+++ b/audio/filter/af_resample.c
@@ -0,0 +1,394 @@
+/*
+ * This audio filter changes the sample rate.
+ *
+ * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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 <inttypes.h>
+
+#include "libavutil/common.h"
+#include "libavutil/mathematics.h"
+#include "af.h"
+#include "dsp.h"
+
+/* Below definition selects the length of each poly phase component.
+ Valid definitions are L8 and L16, where the number denotes the
+ length of the filter. This definition affects the computational
+ complexity (see play()), the performance (see filter.h) and the
+ memory usage. The filter length is chosen to 8 if the machine is
+ slow and to 16 if the machine is fast and has MMX.
+*/
+
+#if !HAVE_MMX // This machine is slow
+#define L8
+#else
+#define L16
+#endif
+
+#include "af_resample_template.c"
+
+// Filtering types
+#define RSMP_LIN (0<<0) // Linear interpolation
+#define RSMP_INT (1<<0) // 16 bit integer
+#define RSMP_FLOAT (2<<0) // 32 bit floating point
+#define RSMP_MASK (3<<0)
+
+// Defines for sloppy or exact resampling
+#define FREQ_SLOPPY (0<<2)
+#define FREQ_EXACT (1<<2)
+#define FREQ_MASK (1<<2)
+
+// Accuracy for linear interpolation
+#define STEPACCURACY 32
+
+// local data
+typedef struct af_resample_s
+{
+ void* w; // Current filter weights
+ void** xq; // Circular buffers
+ uint32_t xi; // Index for circular buffers
+ uint32_t wi; // Index for w
+ uint32_t i; // Number of new samples to put in x queue
+ uint32_t dn; // Down sampling factor
+ uint32_t up; // Up sampling factor
+ uint64_t step; // Step size for linear interpolation
+ uint64_t pt; // Pointer remainder for linear interpolation
+ int setup; // Setup parameters cmdline or through postcreate
+} af_resample_t;
+
+// Fast linear interpolation resample with modest audio quality
+static int linint(struct mp_audio* c,struct mp_audio* l, af_resample_t* s)
+{
+ uint32_t len = 0; // Number of input samples
+ uint32_t nch = l->nch; // Words pre transfer
+ uint64_t step = s->step;
+ int16_t* in16 = ((int16_t*)c->audio);
+ int16_t* out16 = ((int16_t*)l->audio);
+ int32_t* in32 = ((int32_t*)c->audio);
+ int32_t* out32 = ((int32_t*)l->audio);
+ uint64_t end = ((((uint64_t)c->len)/2LL)<<STEPACCURACY);
+ uint64_t pt = s->pt;
+ uint16_t tmp;
+
+ switch (nch){
+ case 1:
+ while(pt < end){
+ out16[len++]=in16[pt>>STEPACCURACY];
+ pt+=step;
+ }
+ s->pt=pt & ((1LL<<STEPACCURACY)-1);
+ break;
+ case 2:
+ end/=2;
+ while(pt < end){
+ out32[len++]=in32[pt>>STEPACCURACY];
+ pt+=step;
+ }
+ len=(len<<1);
+ s->pt=pt & ((1LL<<STEPACCURACY)-1);
+ break;
+ default:
+ end /=nch;
+ while(pt < end){
+ tmp=nch;
+ do {
+ tmp--;
+ out16[len+tmp]=in16[tmp+(pt>>STEPACCURACY)*nch];
+ } while (tmp);
+ len+=nch;
+ pt+=step;
+ }
+ s->pt=pt & ((1LL<<STEPACCURACY)-1);
+ }
+ return len;
+}
+
+/* Determine resampling type and format */
+static int set_types(struct af_instance* af, struct mp_audio* data)
+{
+ af_resample_t* s = af->setup;
+ int rv = AF_OK;
+ float rd = 0;
+
+ // Make sure this filter isn't redundant
+ if((af->data->rate == data->rate) || (af->data->rate == 0))
+ return AF_DETACH;
+ /* If sloppy and small resampling difference (2%) */
+ rd = abs((float)af->data->rate - (float)data->rate)/(float)data->rate;
+ if((((s->setup & FREQ_MASK) == FREQ_SLOPPY) && (rd < 0.02) &&
+ (data->format != (AF_FORMAT_FLOAT_NE))) ||
+ ((s->setup & RSMP_MASK) == RSMP_LIN)){
+ s->setup = (s->setup & ~RSMP_MASK) | RSMP_LIN;
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ mp_msg(MSGT_AFILTER, MSGL_V, "[resample] Using linear interpolation. \n");
+ }
+ else{
+ /* If the input format is float or if float is explicitly selected
+ use float, otherwise use int */
+ if((data->format == (AF_FORMAT_FLOAT_NE)) ||
+ ((s->setup & RSMP_MASK) == RSMP_FLOAT)){
+ s->setup = (s->setup & ~RSMP_MASK) | RSMP_FLOAT;
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ }
+ else{
+ s->setup = (s->setup & ~RSMP_MASK) | RSMP_INT;
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ }
+ mp_msg(MSGT_AFILTER, MSGL_V, "[resample] Using %s processing and %s frequecy"
+ " conversion.\n",
+ ((s->setup & RSMP_MASK) == RSMP_FLOAT)?"floating point":"integer",
+ ((s->setup & FREQ_MASK) == FREQ_SLOPPY)?"inexact":"exact");
+ }
+
+ if(af->data->format != data->format || af->data->bps != data->bps)
+ rv = AF_FALSE;
+ data->format = af->data->format;
+ data->bps = af->data->bps;
+ af->data->nch = data->nch;
+ return rv;
+}
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ af_resample_t* s = af->setup;
+ struct mp_audio* n = arg; // New configuration
+ int i,d = 0;
+ int rv = AF_OK;
+
+ // Free space for circular buffers
+ if(s->xq){
+ free(s->xq[0]);
+ free(s->xq);
+ s->xq = NULL;
+ }
+
+ if(AF_DETACH == (rv = set_types(af,n)))
+ return AF_DETACH;
+
+ // If linear interpolation
+ if((s->setup & RSMP_MASK) == RSMP_LIN){
+ s->pt=0LL;
+ s->step=((uint64_t)n->rate<<STEPACCURACY)/(uint64_t)af->data->rate+1LL;
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "[resample] Linear interpolation step: 0x%016"PRIX64".\n",
+ s->step);
+ af->mul = (double)af->data->rate / n->rate;
+ return rv;
+ }
+
+ // Calculate up and down sampling factors
+ d=av_gcd(af->data->rate,n->rate);
+
+ // If sloppy resampling is enabled limit the upsampling factor
+ if(((s->setup & FREQ_MASK) == FREQ_SLOPPY) && (af->data->rate/d > 5000)){
+ int up=af->data->rate/2;
+ int dn=n->rate/2;
+ int m=2;
+ while(af->data->rate/(d*m) > 5000){
+ d=av_gcd(up,dn);
+ up/=2; dn/=2; m*=2;
+ }
+ d*=m;
+ }
+
+ // Create space for circular buffers
+ s->xq = malloc(n->nch*sizeof(void*));
+ s->xq[0] = calloc(n->nch, 2*L*af->data->bps);
+ for(i=1;i<n->nch;i++)
+ s->xq[i] = (uint8_t *)s->xq[i-1] + 2*L*af->data->bps;
+ s->xi = 0;
+
+ // Check if the design needs to be redone
+ if(s->up != af->data->rate/d || s->dn != n->rate/d){
+ float* w;
+ float* wt;
+ float fc;
+ int j;
+ s->up = af->data->rate/d;
+ s->dn = n->rate/d;
+ s->wi = 0;
+ s->i = 0;
+
+ // Calculate cutoff frequency for filter
+ fc = 1/(float)(max(s->up,s->dn));
+ // Allocate space for polyphase filter bank and prototype filter
+ w = malloc(sizeof(float) * s->up *L);
+ free(s->w);
+ s->w = malloc(L*s->up*af->data->bps);
+
+ // Design prototype filter type using Kaiser window with beta = 10
+ if(NULL == w || NULL == s->w ||
+ -1 == af_filter_design_fir(s->up*L, w, &fc, LP|KAISER , 10.0)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[resample] Unable to design prototype filter.\n");
+ return AF_ERROR;
+ }
+ // Copy data from prototype to polyphase filter
+ wt=w;
+ for(j=0;j<L;j++){//Columns
+ for(i=0;i<s->up;i++){//Rows
+ if((s->setup & RSMP_MASK) == RSMP_INT){
+ float t=(float)s->up*32767.0*(*wt);
+ ((int16_t*)s->w)[i*L+j] = (int16_t)((t>=0.0)?(t+0.5):(t-0.5));
+ }
+ else
+ ((float*)s->w)[i*L+j] = (float)s->up*(*wt);
+ wt++;
+ }
+ }
+ free(w);
+ mp_msg(MSGT_AFILTER, MSGL_V, "[resample] New filter designed up: %i "
+ "down: %i\n", s->up, s->dn);
+ }
+
+ // Set multiplier and delay
+ af->delay = 0; // not set correctly, but shouldn't be too large anyway
+ af->mul = (double)s->up / s->dn;
+ return rv;
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ af_resample_t* s = af->setup;
+ int rate=0;
+ int type=RSMP_INT;
+ int sloppy=1;
+ sscanf((char*)arg,"%i:%i:%i", &rate, &sloppy, &type);
+ s->setup = (sloppy?FREQ_SLOPPY:FREQ_EXACT) |
+ (clamp(type,RSMP_LIN,RSMP_FLOAT));
+ return af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET, &rate);
+ }
+ case AF_CONTROL_POST_CREATE:
+ if((((struct af_cfg*)arg)->force & AF_INIT_FORMAT_MASK) == AF_INIT_FLOAT)
+ ((af_resample_t*)af->setup)->setup = RSMP_FLOAT;
+ return AF_OK;
+ case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET:
+ // Reinit must be called after this function has been called
+
+ // Sanity check
+ if(((int*)arg)[0] < 8000 || ((int*)arg)[0] > 192000){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[resample] The output sample frequency "
+ "must be between 8kHz and 192kHz. Current value is %i \n",
+ ((int*)arg)[0]);
+ return AF_ERROR;
+ }
+
+ af->data->rate=((int*)arg)[0];
+ mp_msg(MSGT_AFILTER, MSGL_V, "[resample] Changing sample rate "
+ "to %iHz\n",af->data->rate);
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ af_resample_t *s = af->setup;
+ if (s) {
+ if (s->xq) free(s->xq[0]);
+ free(s->xq);
+ free(s->w);
+ free(s);
+ }
+ if(af->data)
+ free(af->data->audio);
+ free(af->data);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ int len = 0; // Length of output data
+ struct mp_audio* c = data; // Current working data
+ struct mp_audio* l = af->data; // Local data
+ af_resample_t* s = af->setup;
+
+ if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
+ return NULL;
+
+ // Run resampling
+ switch(s->setup & RSMP_MASK){
+ case(RSMP_INT):
+# define FORMAT_I 1
+ if(s->up>s->dn){
+# define UP
+# include "af_resample_template.c"
+# undef UP
+ }
+ else{
+# define DN
+# include "af_resample_template.c"
+# undef DN
+ }
+ break;
+ case(RSMP_FLOAT):
+# undef FORMAT_I
+# define FORMAT_F 1
+ if(s->up>s->dn){
+# define UP
+# include "af_resample_template.c"
+# undef UP
+ }
+ else{
+# define DN
+# include "af_resample_template.c"
+# undef DN
+ }
+ break;
+ case(RSMP_LIN):
+ len = linint(c, l, s);
+ break;
+ }
+
+ // Set output data
+ c->audio = l->audio;
+ c->len = len*l->bps;
+ c->rate = l->rate;
+
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_resample_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ ((af_resample_t*)af->setup)->setup = RSMP_INT | FREQ_SLOPPY;
+ return AF_OK;
+}
+
+// Description of this plugin
+struct af_info af_info_resample = {
+ "Sample frequency conversion",
+ "resample",
+ "Anders",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_resample_template.c b/audio/filter/af_resample_template.c
new file mode 100644
index 0000000000..4d4c5922ca
--- /dev/null
+++ b/audio/filter/af_resample_template.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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.
+ */
+
+/* This file contains the resampling engine, the sample format is
+ controlled by the FORMAT parameter, the filter length by the L
+ parameter and the resampling type by UP and DN. This file should
+ only be included by af_resample.c
+*/
+
+#undef L
+#undef SHIFT
+#undef FORMAT
+#undef FIR
+#undef ADDQUE
+
+/* The length Lxx definition selects the length of each poly phase
+ component. Valid definitions are L8 and L16 where the number
+ defines the nuber of taps. This definition affects the
+ computational complexity, the performance and the memory usage.
+*/
+
+/* The FORMAT_x parameter selects the sample format type currently
+ float and int16 are supported. Thes two formats are selected by
+ defining eiter FORMAT_F or FORMAT_I. The advantage of using float
+ is that the amplitude and therefore the SNR isn't affected by the
+ filtering, the disadvantage is that it is a lot slower.
+*/
+
+#if defined(FORMAT_I)
+#define SHIFT >>16
+#define FORMAT int16_t
+#else
+#define SHIFT
+#define FORMAT float
+#endif
+
+// Short filter
+#if defined(L8)
+
+#define L 8 // Filter length
+// Unrolled loop to speed up execution
+#define FIR(x,w,y) \
+ (y[0]) = ( w[0]*x[0]+w[1]*x[1]+w[2]*x[2]+w[3]*x[3] \
+ + w[4]*x[4]+w[5]*x[5]+w[6]*x[6]+w[7]*x[7] ) SHIFT
+
+
+
+#else /* L8/L16 */
+
+#define L 16
+// Unrolled loop to speed up execution
+#define FIR(x,w,y) \
+ y[0] = ( w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] \
+ + w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] \
+ + w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11] \
+ + w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15] ) SHIFT
+
+#endif /* L8/L16 */
+
+// Macro to add data to circular que
+#define ADDQUE(xi,xq,in)\
+ xq[xi]=xq[(xi)+L]=*(in);\
+ xi=((xi)-1)&(L-1);
+
+#if defined(UP)
+
+ uint32_t ci = l->nch; // Index for channels
+ uint32_t nch = l->nch; // Number of channels
+ uint32_t inc = s->up/s->dn;
+ uint32_t level = s->up%s->dn;
+ uint32_t up = s->up;
+ uint32_t dn = s->dn;
+ uint32_t ns = c->len/l->bps;
+ register FORMAT* w = s->w;
+
+ register uint32_t wi = 0;
+ register uint32_t xi = 0;
+
+ // Index current channel
+ while(ci--){
+ // Temporary pointers
+ register FORMAT* x = s->xq[ci];
+ register FORMAT* in = ((FORMAT*)c->audio)+ci;
+ register FORMAT* out = ((FORMAT*)l->audio)+ci;
+ FORMAT* end = in+ns; // Block loop end
+ wi = s->wi; xi = s->xi;
+
+ while(in < end){
+ register uint32_t i = inc;
+ if(wi<level) i++;
+
+ ADDQUE(xi,x,in);
+ in+=nch;
+ while(i--){
+ // Run the FIR filter
+ FIR((&x[xi]),(&w[wi*L]),out);
+ len++; out+=nch;
+ // Update wi to point at the correct polyphase component
+ wi=(wi+dn)%up;
+ }
+ }
+
+ }
+ // Save values that needs to be kept for next time
+ s->wi = wi;
+ s->xi = xi;
+#endif /* UP */
+
+#if defined(DN) /* DN */
+ uint32_t ci = l->nch; // Index for channels
+ uint32_t nch = l->nch; // Number of channels
+ uint32_t inc = s->dn/s->up;
+ uint32_t level = s->dn%s->up;
+ uint32_t up = s->up;
+ uint32_t dn = s->dn;
+ uint32_t ns = c->len/l->bps;
+ FORMAT* w = s->w;
+
+ register int32_t i = 0;
+ register uint32_t wi = 0;
+ register uint32_t xi = 0;
+
+ // Index current channel
+ while(ci--){
+ // Temporary pointers
+ register FORMAT* x = s->xq[ci];
+ register FORMAT* in = ((FORMAT*)c->audio)+ci;
+ register FORMAT* out = ((FORMAT*)l->audio)+ci;
+ register FORMAT* end = in+ns; // Block loop end
+ i = s->i; wi = s->wi; xi = s->xi;
+
+ while(in < end){
+
+ ADDQUE(xi,x,in);
+ in+=nch;
+ if((--i)<=0){
+ // Run the FIR filter
+ FIR((&x[xi]),(&w[wi*L]),out);
+ len++; out+=nch;
+
+ // Update wi to point at the correct polyphase component
+ wi=(wi+dn)%up;
+
+ // Insert i number of new samples in queue
+ i = inc;
+ if(wi<level) i++;
+ }
+ }
+ }
+ // Save values that needs to be kept for next time
+ s->wi = wi;
+ s->xi = xi;
+ s->i = i;
+#endif /* DN */
diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c
new file mode 100644
index 0000000000..0bbc220997
--- /dev/null
+++ b/audio/filter/af_scaletempo.c
@@ -0,0 +1,581 @@
+/*
+ * scaletempo audio filter
+ *
+ * scale tempo while maintaining pitch
+ * (WSOLA technique with cross correlation)
+ * inspired by SoundTouch library by Olli Parviainen
+ *
+ * basic algorithm
+ * - produce 'stride' output samples per loop
+ * - consume stride*scale input samples per loop
+ *
+ * to produce smoother transitions between strides, blend next overlap
+ * samples from last stride with correlated samples of current input
+ *
+ * Copyright (c) 2007 Robert Juliano
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+
+#include "af.h"
+#include "libavutil/common.h"
+#include "subopt-helper.h"
+
+// Data for specific instances of this filter
+typedef struct af_scaletempo_s
+{
+ // stride
+ float scale;
+ float speed;
+ float frames_stride_scaled;
+ float frames_stride_error;
+ int bytes_per_frame;
+ int bytes_stride;
+ float bytes_stride_scaled;
+ int bytes_queue;
+ int bytes_queued;
+ int bytes_to_slide;
+ int8_t* buf_queue;
+ // overlap
+ int samples_overlap;
+ int samples_standing;
+ int bytes_overlap;
+ int bytes_standing;
+ void* buf_overlap;
+ void* table_blend;
+ void (*output_overlap)(struct af_scaletempo_s* s, void* out_buf, int bytes_off);
+ // best overlap
+ int frames_search;
+ int num_channels;
+ void* buf_pre_corr;
+ void* table_window;
+ int (*best_overlap_offset)(struct af_scaletempo_s* s);
+ // command line
+ float scale_nominal;
+ float ms_stride;
+ float percent_overlap;
+ float ms_search;
+ short speed_tempo;
+ short speed_pitch;
+} af_scaletempo_t;
+
+static int fill_queue(struct af_instance* af, struct mp_audio* data, int offset)
+{
+ af_scaletempo_t* s = af->setup;
+ int bytes_in = data->len - offset;
+ int offset_unchanged = offset;
+
+ if (s->bytes_to_slide > 0) {
+ if (s->bytes_to_slide < s->bytes_queued) {
+ int bytes_move = s->bytes_queued - s->bytes_to_slide;
+ memmove(s->buf_queue,
+ s->buf_queue + s->bytes_to_slide,
+ bytes_move);
+ s->bytes_to_slide = 0;
+ s->bytes_queued = bytes_move;
+ } else {
+ int bytes_skip;
+ s->bytes_to_slide -= s->bytes_queued;
+ bytes_skip = FFMIN(s->bytes_to_slide, bytes_in);
+ s->bytes_queued = 0;
+ s->bytes_to_slide -= bytes_skip;
+ offset += bytes_skip;
+ bytes_in -= bytes_skip;
+ }
+ }
+
+ if (bytes_in > 0) {
+ int bytes_copy = FFMIN(s->bytes_queue - s->bytes_queued, bytes_in);
+ assert(bytes_copy >= 0);
+ memcpy(s->buf_queue + s->bytes_queued,
+ (int8_t*)data->audio + offset,
+ bytes_copy);
+ s->bytes_queued += bytes_copy;
+ offset += bytes_copy;
+ }
+
+ return offset - offset_unchanged;
+}
+
+#define UNROLL_PADDING (4*4)
+
+static int best_overlap_offset_float(af_scaletempo_t* s)
+{
+ float *pw, *po, *ppc, *search_start;
+ float best_corr = INT_MIN;
+ int best_off = 0;
+ int i, off;
+
+ pw = s->table_window;
+ po = s->buf_overlap;
+ po += s->num_channels;
+ ppc = s->buf_pre_corr;
+ for (i=s->num_channels; i<s->samples_overlap; i++) {
+ *ppc++ = *pw++ * *po++;
+ }
+
+ search_start = (float*)s->buf_queue + s->num_channels;
+ for (off=0; off<s->frames_search; off++) {
+ float corr = 0;
+ float* ps = search_start;
+ ppc = s->buf_pre_corr;
+ for (i=s->num_channels; i<s->samples_overlap; i++) {
+ corr += *ppc++ * *ps++;
+ }
+ if (corr > best_corr) {
+ best_corr = corr;
+ best_off = off;
+ }
+ search_start += s->num_channels;
+ }
+
+ return best_off * 4 * s->num_channels;
+}
+
+static int best_overlap_offset_s16(af_scaletempo_t* s)
+{
+ int32_t *pw, *ppc;
+ int16_t *po, *search_start;
+ int64_t best_corr = INT64_MIN;
+ int best_off = 0;
+ int off;
+ long i;
+
+ pw = s->table_window;
+ po = s->buf_overlap;
+ po += s->num_channels;
+ ppc = s->buf_pre_corr;
+ for (i=s->num_channels; i<s->samples_overlap; i++) {
+ *ppc++ = ( *pw++ * *po++ ) >> 15;
+ }
+
+ search_start = (int16_t*)s->buf_queue + s->num_channels;
+ for (off=0; off<s->frames_search; off++) {
+ int64_t corr = 0;
+ int16_t* ps = search_start;
+ ppc = s->buf_pre_corr;
+ ppc += s->samples_overlap - s->num_channels;
+ ps += s->samples_overlap - s->num_channels;
+ i = -(s->samples_overlap - s->num_channels);
+ do {
+ corr += ppc[i+0] * ps[i+0];
+ corr += ppc[i+1] * ps[i+1];
+ corr += ppc[i+2] * ps[i+2];
+ corr += ppc[i+3] * ps[i+3];
+ i += 4;
+ } while (i < 0);
+ if (corr > best_corr) {
+ best_corr = corr;
+ best_off = off;
+ }
+ search_start += s->num_channels;
+ }
+
+ return best_off * 2 * s->num_channels;
+}
+
+static void output_overlap_float(af_scaletempo_t* s, void* buf_out,
+ int bytes_off)
+{
+ float* pout = buf_out;
+ float* pb = s->table_blend;
+ float* po = s->buf_overlap;
+ float* pin = (float*)(s->buf_queue + bytes_off);
+ int i;
+ for (i=0; i<s->samples_overlap; i++) {
+ *pout++ = *po - *pb++ * ( *po - *pin++ ); po++;
+ }
+}
+static void output_overlap_s16(af_scaletempo_t* s, void* buf_out,
+ int bytes_off)
+{
+ int16_t* pout = buf_out;
+ int32_t* pb = s->table_blend;
+ int16_t* po = s->buf_overlap;
+ int16_t* pin = (int16_t*)(s->buf_queue + bytes_off);
+ int i;
+ for (i=0; i<s->samples_overlap; i++) {
+ *pout++ = *po - ( ( *pb++ * ( *po - *pin++ ) ) >> 16 ); po++;
+ }
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ af_scaletempo_t* s = af->setup;
+ int offset_in;
+ int max_bytes_out;
+ int8_t* pout;
+
+ if (s->scale == 1.0) {
+ af->delay = 0;
+ return data;
+ }
+
+ // RESIZE_LOCAL_BUFFER - can't use macro
+ max_bytes_out = ((int)(data->len / s->bytes_stride_scaled) + 1) * s->bytes_stride;
+ if (max_bytes_out > af->data->len) {
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
+ "old len = %i, new len = %i\n",af->info->name,af->data->len,max_bytes_out);
+ af->data->audio = realloc(af->data->audio, max_bytes_out);
+ if (!af->data->audio) {
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory\n");
+ return NULL;
+ }
+ af->data->len = max_bytes_out;
+ }
+
+ offset_in = fill_queue(af, data, 0);
+ pout = af->data->audio;
+ while (s->bytes_queued >= s->bytes_queue) {
+ int ti;
+ float tf;
+ int bytes_off = 0;
+
+ // output stride
+ if (s->output_overlap) {
+ if (s->best_overlap_offset)
+ bytes_off = s->best_overlap_offset(s);
+ s->output_overlap(s, pout, bytes_off);
+ }
+ memcpy(pout + s->bytes_overlap,
+ s->buf_queue + bytes_off + s->bytes_overlap,
+ s->bytes_standing);
+ pout += s->bytes_stride;
+
+ // input stride
+ memcpy(s->buf_overlap,
+ s->buf_queue + bytes_off + s->bytes_stride,
+ s->bytes_overlap);
+ tf = s->frames_stride_scaled + s->frames_stride_error;
+ ti = (int)tf;
+ s->frames_stride_error = tf - ti;
+ s->bytes_to_slide = ti * s->bytes_per_frame;
+
+ offset_in += fill_queue(af, data, offset_in);
+ }
+
+ // This filter can have a negative delay when scale > 1:
+ // output corresponding to some length of input can be decided and written
+ // after receiving only a part of that input.
+ af->delay = s->bytes_queued - s->bytes_to_slide;
+
+ data->audio = af->data->audio;
+ data->len = pout - (int8_t *)af->data->audio;
+ return data;
+}
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_scaletempo_t* s = af->setup;
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ struct mp_audio* data = (struct mp_audio*)arg;
+ float srate = data->rate / 1000;
+ int nch = data->nch;
+ int bps;
+ int use_int = 0;
+ int frames_stride, frames_overlap;
+ int i, j;
+
+ mp_msg(MSGT_AFILTER, MSGL_V,
+ "[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
+ s->speed, s->scale_nominal, s->scale);
+
+ if (s->scale == 1.0) {
+ if (s->speed_tempo && s->speed_pitch)
+ return AF_DETACH;
+ memcpy(af->data, data, sizeof(struct mp_audio));
+ af->delay = 0;
+ af->mul = 1;
+ return af_test_output(af, data);
+ }
+
+ af->data->rate = data->rate;
+ af->data->nch = data->nch;
+ if ( data->format == AF_FORMAT_S16_LE
+ || data->format == AF_FORMAT_S16_BE ) {
+ use_int = 1;
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = bps = 2;
+ } else {
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = bps = 4;
+ }
+
+ frames_stride = srate * s->ms_stride;
+ s->bytes_stride = frames_stride * bps * nch;
+ s->bytes_stride_scaled = s->scale * s->bytes_stride;
+ s->frames_stride_scaled = s->scale * frames_stride;
+ s->frames_stride_error = 0;
+ af->mul = (double)s->bytes_stride / s->bytes_stride_scaled;
+ af->delay = 0;
+
+ frames_overlap = frames_stride * s->percent_overlap;
+ if (frames_overlap <= 0) {
+ s->bytes_standing = s->bytes_stride;
+ s->samples_standing = s->bytes_standing / bps;
+ s->output_overlap = NULL;
+ s->bytes_overlap = 0;
+ } else {
+ s->samples_overlap = frames_overlap * nch;
+ s->bytes_overlap = frames_overlap * nch * bps;
+ s->bytes_standing = s->bytes_stride - s->bytes_overlap;
+ s->samples_standing = s->bytes_standing / bps;
+ s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap);
+ s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4);
+ if(!s->buf_overlap || !s->table_blend) {
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
+ return AF_ERROR;
+ }
+ memset(s->buf_overlap, 0, s->bytes_overlap);
+ if (use_int) {
+ int32_t* pb = s->table_blend;
+ int64_t blend = 0;
+ for (i=0; i<frames_overlap; i++) {
+ int32_t v = blend / frames_overlap;
+ for (j=0; j<nch; j++) {
+ *pb++ = v;
+ }
+ blend += 65536; // 2^16
+ }
+ s->output_overlap = output_overlap_s16;
+ } else {
+ float* pb = s->table_blend;
+ for (i=0; i<frames_overlap; i++) {
+ float v = i / (float)frames_overlap;
+ for (j=0; j<nch; j++) {
+ *pb++ = v;
+ }
+ }
+ s->output_overlap = output_overlap_float;
+ }
+ }
+
+ s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0;
+ if (s->frames_search <= 0) {
+ s->best_overlap_offset = NULL;
+ } else {
+ if (use_int) {
+ int64_t t = frames_overlap;
+ int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2
+ int32_t* pw;
+ s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap * 2 + UNROLL_PADDING);
+ s->table_window = realloc(s->table_window, s->bytes_overlap * 2 - nch * bps * 2);
+ if(!s->buf_pre_corr || !s->table_window) {
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
+ return AF_ERROR;
+ }
+ memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0, UNROLL_PADDING);
+ pw = s->table_window;
+ for (i=1; i<frames_overlap; i++) {
+ int32_t v = ( i * (t - i) * n ) >> 15;
+ for (j=0; j<nch; j++) {
+ *pw++ = v;
+ }
+ }
+ s->best_overlap_offset = best_overlap_offset_s16;
+ } else {
+ float* pw;
+ s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap);
+ s->table_window = realloc(s->table_window, s->bytes_overlap - nch * bps);
+ if(!s->buf_pre_corr || !s->table_window) {
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
+ return AF_ERROR;
+ }
+ pw = s->table_window;
+ for (i=1; i<frames_overlap; i++) {
+ float v = i * (frames_overlap - i);
+ for (j=0; j<nch; j++) {
+ *pw++ = v;
+ }
+ }
+ s->best_overlap_offset = best_overlap_offset_float;
+ }
+ }
+
+ s->bytes_per_frame = bps * nch;
+ s->num_channels = nch;
+
+ s->bytes_queue
+ = (s->frames_search + frames_stride + frames_overlap) * bps * nch;
+ s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING);
+ if(!s->buf_queue) {
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n");
+ return AF_ERROR;
+ }
+
+ s->bytes_queued = 0;
+ s->bytes_to_slide = 0;
+
+ mp_msg (MSGT_AFILTER, MSGL_DBG2, "[scaletempo] "
+ "%.2f stride_in, %i stride_out, %i standing, "
+ "%i overlap, %i search, %i queue, %s mode\n",
+ s->frames_stride_scaled,
+ (int)(s->bytes_stride / nch / bps),
+ (int)(s->bytes_standing / nch / bps),
+ (int)(s->bytes_overlap / nch / bps),
+ s->frames_search,
+ (int)(s->bytes_queue / nch / bps),
+ (use_int?"s16":"float"));
+
+ return af_test_output(af, (struct mp_audio*)arg);
+ }
+ case AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET:{
+ if (s->speed_tempo) {
+ if (s->speed_pitch) {
+ break;
+ }
+ s->speed = *(float*)arg;
+ s->scale = s->speed * s->scale_nominal;
+ } else {
+ if (s->speed_pitch) {
+ s->speed = 1 / *(float*)arg;
+ s->scale = s->speed * s->scale_nominal;
+ break;
+ }
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:{
+ s->scale = *(float*)arg;
+ s->scale = s->speed * s->scale_nominal;
+ return AF_OK;
+ }
+ case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET:
+ *(float*)arg = s->scale;
+ return AF_OK;
+ case AF_CONTROL_COMMAND_LINE:{
+ strarg_t speed = {};
+ opt_t subopts[] = {
+ {"scale", OPT_ARG_FLOAT, &s->scale_nominal, NULL},
+ {"stride", OPT_ARG_FLOAT, &s->ms_stride, NULL},
+ {"overlap", OPT_ARG_FLOAT, &s->percent_overlap, NULL},
+ {"search", OPT_ARG_FLOAT, &s->ms_search, NULL},
+ {"speed", OPT_ARG_STR, &speed, NULL},
+ {NULL},
+ };
+ if (subopt_parse(arg, subopts) != 0) {
+ return AF_ERROR;
+ }
+ if (s->scale_nominal <= 0) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[scaletempo] %s: %s: scale > 0\n",
+ mp_gtext("error parsing command line"),
+ mp_gtext("value out of range"));
+ return AF_ERROR;
+ }
+ if (s->ms_stride <= 0) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[scaletempo] %s: %s: stride > 0\n",
+ mp_gtext("error parsing command line"),
+ mp_gtext("value out of range"));
+ return AF_ERROR;
+ }
+ if (s->percent_overlap < 0 || s->percent_overlap > 1) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[scaletempo] %s: %s: 0 <= overlap <= 1\n",
+ mp_gtext("error parsing command line"),
+ mp_gtext("value out of range"));
+ return AF_ERROR;
+ }
+ if (s->ms_search < 0) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[scaletempo] %s: %s: search >= 0\n",
+ mp_gtext("error parsing command line"),
+ mp_gtext("value out of range"));
+ return AF_ERROR;
+ }
+ if (speed.len > 0) {
+ if (strcmp(speed.str, "pitch") == 0) {
+ s->speed_tempo = 0;
+ s->speed_pitch = 1;
+ } else if (strcmp(speed.str, "tempo") == 0) {
+ s->speed_tempo = 1;
+ s->speed_pitch = 0;
+ } else if (strcmp(speed.str, "none") == 0) {
+ s->speed_tempo = 0;
+ s->speed_pitch = 0;
+ } else if (strcmp(speed.str, "both") == 0) {
+ s->speed_tempo = 1;
+ s->speed_pitch = 1;
+ } else {
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[scaletempo] %s: %s: speed=[pitch|tempo|none|both]\n",
+ mp_gtext("error parsing command line"),
+ mp_gtext("value out of range"));
+ return AF_ERROR;
+ }
+ }
+ s->scale = s->speed * s->scale_nominal;
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "[scaletempo] %6.3f scale, %6.2f stride, %6.2f overlap, %6.2f search, speed = %s\n", s->scale_nominal, s->ms_stride, s->percent_overlap, s->ms_search, (s->speed_tempo?(s->speed_pitch?"tempo and speed":"tempo"):(s->speed_pitch?"pitch":"none")));
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ af_scaletempo_t* s = af->setup;
+ free(af->data->audio);
+ free(af->data);
+ free(s->buf_queue);
+ free(s->buf_overlap);
+ free(s->buf_pre_corr);
+ free(s->table_blend);
+ free(s->table_window);
+ free(af->setup);
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af_scaletempo_t* s;
+
+ af->control = control;
+ af->uninit = uninit;
+ af->play = play;
+ af->mul = 1;
+ af->data = calloc(1,sizeof(struct mp_audio));
+ af->setup = calloc(1,sizeof(af_scaletempo_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+
+ s = af->setup;
+ s->scale = s->speed = s->scale_nominal = 1.0;
+ s->speed_tempo = 1;
+ s->speed_pitch = 0;
+ s->ms_stride = 60;
+ s->percent_overlap = .20;
+ s->ms_search = 14;
+
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_scaletempo = {
+ "Scale audio tempo while maintaining pitch",
+ "scaletempo",
+ "Robert Juliano",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_sinesuppress.c b/audio/filter/af_sinesuppress.c
new file mode 100644
index 0000000000..36f7189f00
--- /dev/null
+++ b/audio/filter/af_sinesuppress.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2006 Michael Niedermayer
+ * Copyright (C) 2004 Alex Beregszaszi
+ * based upon af_extrastereo.c by Pierre Lombard
+ *
+ * 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 <string.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_sinesuppress_s
+{
+ double freq;
+ double decay;
+ double real;
+ double imag;
+ double ref;
+ double pos;
+}af_sinesuppress_t;
+
+static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data);
+//static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data);
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_sinesuppress_t* s = (af_sinesuppress_t*)af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = 1;
+#if 0
+ if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
+ {
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ af->play = play_float;
+ }// else
+#endif
+ {
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ af->play = play_s16;
+ }
+
+ return af_test_output(af,(struct mp_audio*)arg);
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ float f1,f2;
+ sscanf((char*)arg,"%f:%f", &f1,&f2);
+ s->freq = f1;
+ s->decay = f2;
+ return AF_OK;
+ }
+ case AF_CONTROL_SS_FREQ | AF_CONTROL_SET:
+ s->freq = *(float*)arg;
+ return AF_OK;
+ case AF_CONTROL_SS_FREQ | AF_CONTROL_GET:
+ *(float*)arg = s->freq;
+ return AF_OK;
+ case AF_CONTROL_SS_DECAY | AF_CONTROL_SET:
+ s->decay = *(float*)arg;
+ return AF_OK;
+ case AF_CONTROL_SS_DECAY | AF_CONTROL_GET:
+ *(float*)arg = s->decay;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data)
+{
+ af_sinesuppress_t *s = af->setup;
+ register int i = 0;
+ int16_t *a = (int16_t*)data->audio; // Audio data
+ int len = data->len/2; // Number of samples
+
+ for (i = 0; i < len; i++)
+ {
+ double co= cos(s->pos);
+ double si= sin(s->pos);
+
+ s->real += co * a[i];
+ s->imag += si * a[i];
+ s->ref += co * co;
+
+ a[i] -= (s->real * co + s->imag * si) / s->ref;
+
+ s->real -= s->real * s->decay;
+ s->imag -= s->imag * s->decay;
+ s->ref -= s->ref * s->decay;
+
+ s->pos += 2 * M_PI * s->freq / data->rate;
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "[sinesuppress] f:%8.2f: amp:%8.2f\n", s->freq, sqrt(s->real*s->real + s->imag*s->imag) / s->ref);
+
+ return data;
+}
+
+#if 0
+static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data)
+{
+ af_sinesuppress_t *s = af->setup;
+ register int i = 0;
+ float *a = (float*)data->audio; // Audio data
+ int len = data->len/4; // Number of samples
+ float avg, l, r;
+
+ for (i = 0; i < len; i+=2)
+ {
+ avg = (a[i] + a[i + 1]) / 2;
+
+/* l = avg + (s->mul * (a[i] - avg));
+ r = avg + (s->mul * (a[i + 1] - avg));*/
+
+ a[i] = af_softclip(l);
+ a[i + 1] = af_softclip(r);
+ }
+
+ return data;
+}
+#endif
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play_s16;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_sinesuppress_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+
+ ((af_sinesuppress_t*)af->setup)->freq = 50.0;
+ ((af_sinesuppress_t*)af->setup)->decay = 0.0001;
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_sinesuppress = {
+ "Sine Suppress",
+ "sinesuppress",
+ "Michael Niedermayer",
+ "",
+ 0,
+ af_open
+};
diff --git a/audio/filter/af_sub.c b/audio/filter/af_sub.c
new file mode 100644
index 0000000000..4af28d9141
--- /dev/null
+++ b/audio/filter/af_sub.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au
+ *
+ * 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.
+ */
+
+/* This filter adds a sub-woofer channels to the audio stream by
+ averaging the left and right channel and low-pass filter them. The
+ low-pass filter is implemented as a 4th order IIR Butterworth
+ filter, with a variable cutoff frequency between 10 and 300 Hz. The
+ filter gives 24dB/octave attenuation. There are two runtime
+ controls one for setting which channel to insert the sub-audio into
+ called AF_CONTROL_SUB_CH and one for setting the cutoff frequency
+ called AF_CONTROL_SUB_FC.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "af.h"
+#include "dsp.h"
+
+// Q value for low-pass filter
+#define Q 1.0
+
+// Analog domain biquad section
+typedef struct{
+ float a[3]; // Numerator coefficients
+ float b[3]; // Denominator coefficients
+} biquad_t;
+
+// S-parameters for designing 4th order Butterworth filter
+static biquad_t sp[2] = {{{1.0,0.0,0.0},{1.0,0.765367,1.0}},
+ {{1.0,0.0,0.0},{1.0,1.847759,1.0}}};
+
+// Data for specific instances of this filter
+typedef struct af_sub_s
+{
+ float w[2][4]; // Filter taps for low-pass filter
+ float q[2][2]; // Circular queues
+ float fc; // Cutoff frequency [Hz] for low-pass filter
+ float k; // Filter gain;
+ int ch; // Channel number which to insert the filtered data
+
+}af_sub_t;
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_sub_t* s = af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch);
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+
+ // Design low-pass filter
+ s->k = 1.0;
+ if((-1 == af_filter_szxform(sp[0].a, sp[0].b, Q, s->fc,
+ (float)af->data->rate, &s->k, s->w[0])) ||
+ (-1 == af_filter_szxform(sp[1].a, sp[1].b, Q, s->fc,
+ (float)af->data->rate, &s->k, s->w[1])))
+ return AF_ERROR;
+ return af_test_output(af,(struct mp_audio*)arg);
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ int ch=5;
+ float fc=60.0;
+ sscanf(arg,"%f:%i", &fc , &ch);
+ if(AF_OK != control(af,AF_CONTROL_SUB_CH | AF_CONTROL_SET, &ch))
+ return AF_ERROR;
+ return control(af,AF_CONTROL_SUB_FC | AF_CONTROL_SET, &fc);
+ }
+ case AF_CONTROL_SUB_CH | AF_CONTROL_SET: // Requires reinit
+ // Sanity check
+ if((*(int*)arg >= AF_NCH) || (*(int*)arg < 0)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Subwoofer channel number must be between "
+ " 0 and %i current value is %i\n", AF_NCH-1, *(int*)arg);
+ return AF_ERROR;
+ }
+ s->ch = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_SUB_CH | AF_CONTROL_GET:
+ *(int*)arg = s->ch;
+ return AF_OK;
+ case AF_CONTROL_SUB_FC | AF_CONTROL_SET: // Requires reinit
+ // Sanity check
+ if((*(float*)arg > 300) || (*(float*)arg < 20)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Cutoff frequency must be between 20Hz and"
+ " 300Hz current value is %0.2f",*(float*)arg);
+ return AF_ERROR;
+ }
+ // Set cutoff frequency
+ s->fc = *(float*)arg;
+ return AF_OK;
+ case AF_CONTROL_SUB_FC | AF_CONTROL_GET:
+ *(float*)arg = s->fc;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+#ifndef IIR
+#define IIR(in,w,q,out) { \
+ float h0 = (q)[0]; \
+ float h1 = (q)[1]; \
+ float hn = (in) - h0 * (w)[0] - h1 * (w)[1]; \
+ out = hn + h0 * (w)[2] + h1 * (w)[3]; \
+ (q)[1] = h0; \
+ (q)[0] = hn; \
+}
+#endif
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ af_sub_t* s = af->setup; // Setup for this instance
+ float* a = c->audio; // Audio data
+ int len = c->len/4; // Number of samples in current audio block
+ int nch = c->nch; // Number of channels
+ int ch = s->ch; // Channel in which to insert the sub audio
+ register int i;
+
+ // Run filter
+ for(i=0;i<len;i+=nch){
+ // Average left and right
+ register float x = 0.5 * (a[i] + a[i+1]);
+ IIR(x * s->k, s->w[0], s->q[0], x);
+ IIR(x , s->w[1], s->q[1], a[i+ch]);
+ }
+
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ af_sub_t* s;
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=s=calloc(1,sizeof(af_sub_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ // Set default values
+ s->ch = 5; // Channel nr 6
+ s->fc = 60; // Cutoff frequency 60Hz
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_sub = {
+ "Audio filter for adding a sub-base channel",
+ "sub",
+ "Anders",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_surround.c b/audio/filter/af_surround.c
new file mode 100644
index 0000000000..57288d6ba2
--- /dev/null
+++ b/audio/filter/af_surround.c
@@ -0,0 +1,273 @@
+/*
+ * Filter to do simple decoding of matrixed surround sound.
+ * This will provide a (basic) surround-sound effect from
+ * audio encoded for Dolby Surround, Pro Logic etc.
+ *
+ * original author: Steve Davies <steve@daviesfam.org>
+ *
+ * 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.
+ */
+
+/* The principle: Make rear channels by extracting anti-phase data
+ from the front channels, delay by 20ms and feed to rear in anti-phase
+*/
+
+
+/* SPLITREAR: Define to decode two distinct rear channels - this
+ doesn't work so well in practice because separation in a passive
+ matrix is not high. C (dialogue) to Ls and Rs 14dB or so - so
+ dialogue leaks to the rear. Still - give it a try and send
+ feedback. Comment this define for old behavior of a single
+ surround sent to rear in anti-phase */
+#define SPLITREAR 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "af.h"
+#include "dsp.h"
+
+#define L 32 // Length of fir filter
+#define LD 65536 // Length of delay buffer
+
+// 32 Tap fir filter loop unrolled
+#define FIR(x,w,y) \
+ y = ( w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] \
+ + w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] \
+ + w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11] \
+ + w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15] \
+ + w[16]*x[16]+w[17]*x[17]+w[18]*x[18]+w[19]*x[19] \
+ + w[20]*x[20]+w[21]*x[21]+w[22]*x[22]+w[23]*x[23] \
+ + w[24]*x[24]+w[25]*x[25]+w[26]*x[26]+w[27]*x[27] \
+ + w[28]*x[28]+w[29]*x[29]+w[30]*x[30]+w[31]*x[31])
+
+// Add to circular queue macro + update index
+#ifdef SPLITREAR
+#define ADDQUE(qi,rq,lq,r,l)\
+ lq[qi]=lq[qi+L]=(l);\
+ rq[qi]=rq[qi+L]=(r);\
+ qi=(qi-1)&(L-1);
+#else
+#define ADDQUE(qi,lq,l)\
+ lq[qi]=lq[qi+L]=(l);\
+ qi=(qi-1)&(L-1);
+#endif
+
+// Macro for updating queue index in delay queues
+#define UPDATEQI(qi) qi=(qi+1)&(LD-1)
+
+// instance data
+typedef struct af_surround_s
+{
+ float lq[2*L]; // Circular queue for filtering left rear channel
+ float rq[2*L]; // Circular queue for filtering right rear channel
+ float w[L]; // FIR filter coefficients for surround sound 7kHz low-pass
+ float* dr; // Delay queue right rear channel
+ float* dl; // Delay queue left rear channel
+ float d; // Delay time
+ int i; // Position in circular buffer
+ int wi; // Write index for delay queue
+ int ri; // Read index for delay queue
+}af_surround_t;
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_surround_t *s = af->setup;
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ float fc;
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch*2;
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+
+ if (af->data->nch != 4){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Only stereo input is supported.\n");
+ return AF_DETACH;
+ }
+ // Surround filer coefficients
+ fc = 2.0 * 7000.0/(float)af->data->rate;
+ if (-1 == af_filter_design_fir(L, s->w, &fc, LP|HAMMING, 0)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Unable to design low-pass filter.\n");
+ return AF_ERROR;
+ }
+
+ // Free previous delay queues
+ free(s->dl);
+ free(s->dr);
+ // Allocate new delay queues
+ s->dl = calloc(LD,af->data->bps);
+ s->dr = calloc(LD,af->data->bps);
+ if((NULL == s->dl) || (NULL == s->dr))
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n");
+
+ // Initialize delay queue index
+ if(AF_OK != af_from_ms(1, &s->d, &s->wi, af->data->rate, 0.0, 1000.0))
+ return AF_ERROR;
+// printf("%i\n",s->wi);
+ s->ri = 0;
+
+ if((af->data->format != ((struct mp_audio*)arg)->format) ||
+ (af->data->bps != ((struct mp_audio*)arg)->bps)){
+ ((struct mp_audio*)arg)->format = af->data->format;
+ ((struct mp_audio*)arg)->bps = af->data->bps;
+ return AF_FALSE;
+ }
+ return AF_OK;
+ }
+ case AF_CONTROL_COMMAND_LINE:{
+ float d = 0;
+ sscanf((char*)arg,"%f",&d);
+ if ((d < 0) || (d > 1000)){
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Invalid delay time, valid time values"
+ " are 0ms to 1000ms current value is %0.3f ms\n",d);
+ return AF_ERROR;
+ }
+ s->d = d;
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ if(af->data)
+ free(af->data->audio);
+ free(af->data);
+ free(af->setup);
+}
+
+// The beginnings of an active matrix...
+static float steering_matrix[][12] = {
+// LL RL LR RR LS RS
+// LLs RLs LRs RRs LC RC
+ {.707, .0, .0, .707, .5, -.5,
+ .5878, -.3928, .3928, -.5878, .5, .5},
+};
+
+// Experimental moving average dominance
+//static int amp_L = 0, amp_R = 0, amp_C = 0, amp_S = 0;
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data){
+ af_surround_t* s = (af_surround_t*)af->setup;
+ float* m = steering_matrix[0];
+ float* in = data->audio; // Input audio data
+ float* out = NULL; // Output audio data
+ float* end = in + data->len / sizeof(float); // Loop end
+ int i = s->i; // Filter queue index
+ int ri = s->ri; // Read index for delay queue
+ int wi = s->wi; // Write index for delay queue
+
+ if (AF_OK != RESIZE_LOCAL_BUFFER(af, data))
+ return NULL;
+
+ out = af->data->audio;
+
+ while(in < end){
+ /* Dominance:
+ abs(in[0]) abs(in[1]);
+ abs(in[0]+in[1]) abs(in[0]-in[1]);
+ 10 * log( abs(in[0]) / (abs(in[1])|1) );
+ 10 * log( abs(in[0]+in[1]) / (abs(in[0]-in[1])|1) ); */
+
+ /* About volume balancing...
+ Surround encoding does the following:
+ Lt=L+.707*C+.707*S, Rt=R+.707*C-.707*S
+ So S should be extracted as:
+ (Lt-Rt)
+ But we are splitting the S to two output channels, so we
+ must take 3dB off as we split it:
+ Ls=Rs=.707*(Lt-Rt)
+ Trouble is, Lt could be +1, Rt -1, so possibility that S will
+ overflow. So to avoid that, we cut L/R by 3dB (*.707), and S by
+ 6dB (/2). This keeps the overall balance, but guarantees no
+ overflow. */
+
+ // Output front left and right
+ out[0] = m[0]*in[0] + m[1]*in[1];
+ out[1] = m[2]*in[0] + m[3]*in[1];
+
+ // Low-pass output @ 7kHz
+ FIR((&s->lq[i]), s->w, s->dl[wi]);
+
+ // Delay output by d ms
+ out[2] = s->dl[ri];
+
+#ifdef SPLITREAR
+ // Low-pass output @ 7kHz
+ FIR((&s->rq[i]), s->w, s->dr[wi]);
+
+ // Delay output by d ms
+ out[3] = s->dr[ri];
+#else
+ out[3] = -out[2];
+#endif
+
+ // Update delay queues indexes
+ UPDATEQI(ri);
+ UPDATEQI(wi);
+
+ // Calculate and save surround in circular queue
+#ifdef SPLITREAR
+ ADDQUE(i, s->rq, s->lq, m[6]*in[0]+m[7]*in[1], m[8]*in[0]+m[9]*in[1]);
+#else
+ ADDQUE(i, s->lq, m[4]*in[0]+m[5]*in[1]);
+#endif
+
+ // Next sample...
+ in = &in[data->nch];
+ out = &out[af->data->nch];
+ }
+
+ // Save indexes
+ s->i = i; s->ri = ri; s->wi = wi;
+
+ // Set output data
+ data->audio = af->data->audio;
+ data->len *= 2;
+ data->nch = af->data->nch;
+
+ return data;
+}
+
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=2;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_surround_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ ((af_surround_t*)af->setup)->d = 20;
+ return AF_OK;
+}
+
+struct af_info af_info_surround =
+{
+ "Surround decoder filter",
+ "surround",
+ "Steve Davies <steve@daviesfam.org>",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_sweep.c b/audio/filter/af_sweep.c
new file mode 100644
index 0000000000..6d1106fefc
--- /dev/null
+++ b/audio/filter/af_sweep.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.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 <string.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "config.h"
+#include "af.h"
+
+typedef struct af_sweep_s{
+ double x;
+ double delta;
+}af_sweept;
+
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_sweept* s = (af_sweept*)af->setup;
+ struct mp_audio *data= (struct mp_audio*)arg;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ af->data->nch = data->nch;
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ af->data->rate = data->rate;
+
+ return AF_OK;
+ case AF_CONTROL_COMMAND_LINE:
+ sscanf((char*)arg,"%lf", &s->delta);
+ return AF_OK;
+/* case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET:
+ af->data->rate = *(int*)arg;
+ return AF_OK;*/
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ af_sweept *s = af->setup;
+ int i, j;
+ int16_t *in = (int16_t*)data->audio;
+ int chans = data->nch;
+ int in_len = data->len/(2*chans);
+
+ for(i=0; i<in_len; i++){
+ for(j=0; j<chans; j++)
+ in[i*chans+j]= 32000*sin(s->x*s->x);
+ s->x += s->delta;
+ if(2*s->x*s->delta >= 3.141592) s->x=0;
+ }
+
+ return data;
+}
+
+static int af_open(struct af_instance* af){
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_sweept));
+ return AF_OK;
+}
+
+struct af_info af_info_sweep = {
+ "sine sweep",
+ "sweep",
+ "Michael Niedermayer",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_tools.c b/audio/filter/af_tools.c
new file mode 100644
index 0000000000..0d5dc6c573
--- /dev/null
+++ b/audio/filter/af_tools.c
@@ -0,0 +1,110 @@
+/*
+ * 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 <math.h>
+#include <string.h>
+#include "af.h"
+
+/* Convert to gain value from dB. Returns AF_OK if of and AF_ERROR if
+ fail */
+int af_from_dB(int n, float* in, float* out, float k, float mi, float ma)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out)
+ return AF_ERROR;
+
+ for(i=0;i<n;i++){
+ if(in[i]<=-200)
+ out[i]=0.0;
+ else
+ out[i]=pow(10.0,clamp(in[i],mi,ma)/k);
+ }
+ return AF_OK;
+}
+
+/* Convert from gain value to dB. Returns AF_OK if of and AF_ERROR if
+ fail */
+int af_to_dB(int n, float* in, float* out, float k)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out)
+ return AF_ERROR;
+
+ for(i=0;i<n;i++){
+ if(in[i] == 0.0)
+ out[i]=-200.0;
+ else
+ out[i]=k*log10(in[i]);
+ }
+ return AF_OK;
+}
+
+/* Convert from ms to sample time */
+int af_from_ms(int n, float* in, int* out, int rate, float mi, float ma)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out)
+ return AF_ERROR;
+
+ for(i=0;i<n;i++)
+ out[i]=(int)((float)rate * clamp(in[i],mi,ma)/1000.0);
+
+ return AF_OK;
+}
+
+/* Convert from sample time to ms */
+int af_to_ms(int n, int* in, float* out, int rate)
+{
+ int i = 0;
+ // Sanity check
+ if(!in || !out || !rate)
+ return AF_ERROR;
+
+ for(i=0;i<n;i++)
+ out[i]=1000.0 * (float)in[i]/((float)rate);
+
+ return AF_OK;
+}
+
+/* Helper function for testing the output format */
+int af_test_output(struct af_instance* af, struct mp_audio* out)
+{
+ if((af->data->format != out->format) ||
+ (af->data->bps != out->bps) ||
+ (af->data->rate != out->rate) ||
+ (af->data->nch != out->nch)){
+ memcpy(out,af->data,sizeof(struct mp_audio));
+ return AF_FALSE;
+ }
+ return AF_OK;
+}
+
+/* Soft clipping, the sound of a dream, thanks to Jon Wattes
+ post to Musicdsp.org */
+float af_softclip(float a)
+{
+ if (a >= M_PI/2)
+ return 1.0;
+ else if (a <= -M_PI/2)
+ return -1.0;
+ else
+ return sin(a);
+}
diff --git a/audio/filter/af_volnorm.c b/audio/filter/af_volnorm.c
new file mode 100644
index 0000000000..b4c204d305
--- /dev/null
+++ b/audio/filter/af_volnorm.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard
+ *
+ * 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 <string.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Methods:
+// 1: uses a 1 value memory and coefficients new=a*old+b*cur (with a+b=1)
+// 2: uses several samples to smooth the variations (standard weighted mean
+// on past samples)
+
+// Size of the memory array
+// FIXME: should depend on the frequency of the data (should be a few seconds)
+#define NSAMPLES 128
+
+// If summing all the mem[].len is lower than MIN_SAMPLE_SIZE bytes, then we
+// choose to ignore the computed value as it's not significant enough
+// FIXME: should depend on the frequency of the data (0.5s maybe)
+#define MIN_SAMPLE_SIZE 32000
+
+// mul is the value by which the samples are scaled
+// and has to be in [MUL_MIN, MUL_MAX]
+#define MUL_INIT 1.0
+#define MUL_MIN 0.1
+#define MUL_MAX 5.0
+
+// Silence level
+// FIXME: should be relative to the level of the samples
+#define SIL_S16 (SHRT_MAX * 0.01)
+#define SIL_FLOAT (INT_MAX * 0.01) // FIXME
+
+// smooth must be in ]0.0, 1.0[
+#define SMOOTH_MUL 0.06
+#define SMOOTH_LASTAVG 0.06
+
+#define DEFAULT_TARGET 0.25
+
+// Data for specific instances of this filter
+typedef struct af_volume_s
+{
+ int method; // method used
+ float mul;
+ // method 1
+ float lastavg; // history value of the filter
+ // method 2
+ int idx;
+ struct {
+ float avg; // average level of the sample
+ int len; // sample size (weight)
+ } mem[NSAMPLES];
+ // "Ideal" level
+ float mid_s16;
+ float mid_float;
+}af_volnorm_t;
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_volnorm_t* s = (af_volnorm_t*)af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+
+ if(((struct mp_audio*)arg)->format == (AF_FORMAT_S16_NE)){
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ }else{
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ }
+ return af_test_output(af,(struct mp_audio*)arg);
+ case AF_CONTROL_COMMAND_LINE:{
+ int i = 0;
+ float target = DEFAULT_TARGET;
+ sscanf((char*)arg,"%d:%f", &i, &target);
+ if (i != 1 && i != 2)
+ return AF_ERROR;
+ s->method = i-1;
+ s->mid_s16 = ((float)SHRT_MAX) * target;
+ s->mid_float = ((float)INT_MAX) * target;
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+static void method1_int16(af_volnorm_t *s, struct mp_audio *c)
+{
+ register int i = 0;
+ int16_t *data = (int16_t*)c->audio; // Audio data
+ int len = c->len/2; // Number of samples
+ float curavg = 0.0, newavg, neededmul;
+ int tmp;
+
+ for (i = 0; i < len; i++)
+ {
+ tmp = data[i];
+ curavg += tmp * tmp;
+ }
+ curavg = sqrt(curavg / (float) len);
+
+ // Evaluate an adequate 'mul' coefficient based on previous state, current
+ // samples level, etc
+
+ if (curavg > SIL_S16)
+ {
+ neededmul = s->mid_s16 / (curavg * s->mul);
+ s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
+
+ // clamp the mul coefficient
+ s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
+ }
+
+ // Scale & clamp the samples
+ for (i = 0; i < len; i++)
+ {
+ tmp = s->mul * data[i];
+ tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
+ data[i] = tmp;
+ }
+
+ // Evaulation of newavg (not 100% accurate because of values clamping)
+ newavg = s->mul * curavg;
+
+ // Stores computed values for future smoothing
+ s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg;
+}
+
+static void method1_float(af_volnorm_t *s, struct mp_audio *c)
+{
+ register int i = 0;
+ float *data = (float*)c->audio; // Audio data
+ int len = c->len/4; // Number of samples
+ float curavg = 0.0, newavg, neededmul, tmp;
+
+ for (i = 0; i < len; i++)
+ {
+ tmp = data[i];
+ curavg += tmp * tmp;
+ }
+ curavg = sqrt(curavg / (float) len);
+
+ // Evaluate an adequate 'mul' coefficient based on previous state, current
+ // samples level, etc
+
+ if (curavg > SIL_FLOAT) // FIXME
+ {
+ neededmul = s->mid_float / (curavg * s->mul);
+ s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
+
+ // clamp the mul coefficient
+ s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
+ }
+
+ // Scale & clamp the samples
+ for (i = 0; i < len; i++)
+ data[i] *= s->mul;
+
+ // Evaulation of newavg (not 100% accurate because of values clamping)
+ newavg = s->mul * curavg;
+
+ // Stores computed values for future smoothing
+ s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg;
+}
+
+static void method2_int16(af_volnorm_t *s, struct mp_audio *c)
+{
+ register int i = 0;
+ int16_t *data = (int16_t*)c->audio; // Audio data
+ int len = c->len/2; // Number of samples
+ float curavg = 0.0, newavg, avg = 0.0;
+ int tmp, totallen = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ tmp = data[i];
+ curavg += tmp * tmp;
+ }
+ curavg = sqrt(curavg / (float) len);
+
+ // Evaluate an adequate 'mul' coefficient based on previous state, current
+ // samples level, etc
+ for (i = 0; i < NSAMPLES; i++)
+ {
+ avg += s->mem[i].avg * (float)s->mem[i].len;
+ totallen += s->mem[i].len;
+ }
+
+ if (totallen > MIN_SAMPLE_SIZE)
+ {
+ avg /= (float)totallen;
+ if (avg >= SIL_S16)
+ {
+ s->mul = s->mid_s16 / avg;
+ s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
+ }
+ }
+
+ // Scale & clamp the samples
+ for (i = 0; i < len; i++)
+ {
+ tmp = s->mul * data[i];
+ tmp = clamp(tmp, SHRT_MIN, SHRT_MAX);
+ data[i] = tmp;
+ }
+
+ // Evaulation of newavg (not 100% accurate because of values clamping)
+ newavg = s->mul * curavg;
+
+ // Stores computed values for future smoothing
+ s->mem[s->idx].len = len;
+ s->mem[s->idx].avg = newavg;
+ s->idx = (s->idx + 1) % NSAMPLES;
+}
+
+static void method2_float(af_volnorm_t *s, struct mp_audio *c)
+{
+ register int i = 0;
+ float *data = (float*)c->audio; // Audio data
+ int len = c->len/4; // Number of samples
+ float curavg = 0.0, newavg, avg = 0.0, tmp;
+ int totallen = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ tmp = data[i];
+ curavg += tmp * tmp;
+ }
+ curavg = sqrt(curavg / (float) len);
+
+ // Evaluate an adequate 'mul' coefficient based on previous state, current
+ // samples level, etc
+ for (i = 0; i < NSAMPLES; i++)
+ {
+ avg += s->mem[i].avg * (float)s->mem[i].len;
+ totallen += s->mem[i].len;
+ }
+
+ if (totallen > MIN_SAMPLE_SIZE)
+ {
+ avg /= (float)totallen;
+ if (avg >= SIL_FLOAT)
+ {
+ s->mul = s->mid_float / avg;
+ s->mul = clamp(s->mul, MUL_MIN, MUL_MAX);
+ }
+ }
+
+ // Scale & clamp the samples
+ for (i = 0; i < len; i++)
+ data[i] *= s->mul;
+
+ // Evaulation of newavg (not 100% accurate because of values clamping)
+ newavg = s->mul * curavg;
+
+ // Stores computed values for future smoothing
+ s->mem[s->idx].len = len;
+ s->mem[s->idx].avg = newavg;
+ s->idx = (s->idx + 1) % NSAMPLES;
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ af_volnorm_t *s = af->setup;
+
+ if(af->data->format == (AF_FORMAT_S16_NE))
+ {
+ if (s->method)
+ method2_int16(s, data);
+ else
+ method1_int16(s, data);
+ }
+ else if(af->data->format == (AF_FORMAT_FLOAT_NE))
+ {
+ if (s->method)
+ method2_float(s, data);
+ else
+ method1_float(s, data);
+ }
+ return data;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ int i = 0;
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_volnorm_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+
+ ((af_volnorm_t*)af->setup)->mul = MUL_INIT;
+ ((af_volnorm_t*)af->setup)->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET;
+ ((af_volnorm_t*)af->setup)->idx = 0;
+ ((af_volnorm_t*)af->setup)->mid_s16 = ((float)SHRT_MAX) * DEFAULT_TARGET;
+ ((af_volnorm_t*)af->setup)->mid_float = ((float)INT_MAX) * DEFAULT_TARGET;
+ for (i = 0; i < NSAMPLES; i++)
+ {
+ ((af_volnorm_t*)af->setup)->mem[i].len = 0;
+ ((af_volnorm_t*)af->setup)->mem[i].avg = 0;
+ }
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_volnorm = {
+ "Volume normalizer filter",
+ "volnorm",
+ "Alex Beregszaszi & Pierre Lombard",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/af_volume.c b/audio/filter/af_volume.c
new file mode 100644
index 0000000000..ecf181c8b8
--- /dev/null
+++ b/audio/filter/af_volume.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C)2002 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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.
+ */
+
+/* This audio filter changes the volume of the sound, and can be used
+ when the mixer doesn't support the PCM channel. It can handle
+ between 1 and AF_NCH channels. The volume can be adjusted between -60dB
+ to +20dB and is set on a per channels basis. The is accessed through
+ AF_CONTROL_VOLUME_LEVEL.
+
+ The filter has support for soft-clipping, it is enabled by
+ AF_CONTROL_VOLUME_SOFTCLIPP. It has also a probing feature which
+ can be used to measure the power in the audio stream, both an
+ instantaneous value and the maximum value can be probed. The
+ probing is enable by AF_CONTROL_VOLUME_PROBE_ON_OFF and is done on a
+ per channel basis. The result from the probing is obtained using
+ AF_CONTROL_VOLUME_PROBE_GET and AF_CONTROL_VOLUME_PROBE_GET_MAX. The
+ probed values are calculated in dB.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <limits.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_volume_s
+{
+ int enable[AF_NCH]; // Enable/disable / channel
+ float pow[AF_NCH]; // Estimated power level [dB]
+ float max[AF_NCH]; // Max Power level [dB]
+ float level[AF_NCH]; // Gain level for each channel
+ float time; // Forgetting factor for power estimate
+ int soft; // Enable/disable soft clipping
+ int fast; // Use fix-point volume control
+}af_volume_t;
+
+// Initialization and runtime control
+static int control(struct af_instance* af, int cmd, void* arg)
+{
+ af_volume_t* s = (af_volume_t*)af->setup;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ // Sanity check
+ if(!arg) return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ af->data->nch = ((struct mp_audio*)arg)->nch;
+
+ if(s->fast && (((struct mp_audio*)arg)->format != (AF_FORMAT_FLOAT_NE))){
+ af->data->format = AF_FORMAT_S16_NE;
+ af->data->bps = 2;
+ }
+ else{
+ // Cutoff set to 10Hz for forgetting factor
+ float x = 2.0*M_PI*15.0/(float)af->data->rate;
+ float t = 2.0-cos(x);
+ s->time = 1.0 - (t - sqrt(t*t - 1));
+ mp_msg(MSGT_AFILTER, MSGL_DBG2, "[volume] Forgetting factor = %0.5f\n",s->time);
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+ }
+ return af_test_output(af,(struct mp_audio*)arg);
+ case AF_CONTROL_COMMAND_LINE:{
+ float v=0.0;
+ float vol[AF_NCH];
+ int i;
+ sscanf((char*)arg,"%f:%i", &v, &s->soft);
+ for(i=0;i<AF_NCH;i++) vol[i]=v;
+ return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol);
+ }
+ case AF_CONTROL_POST_CREATE:
+ s->fast = ((((struct af_cfg*)arg)->force & AF_INIT_FORMAT_MASK) ==
+ AF_INIT_FLOAT) ? 0 : 1;
+ return AF_OK;
+ case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_SET:
+ memcpy(s->enable,(int*)arg,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_GET:
+ memcpy((int*)arg,s->enable,AF_NCH*sizeof(int));
+ return AF_OK;
+ case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_SET:
+ s->soft = *(int*)arg;
+ return AF_OK;
+ case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_GET:
+ *(int*)arg = s->soft;
+ return AF_OK;
+ case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET:
+ return af_from_dB(AF_NCH,(float*)arg,s->level,20.0,-200.0,60.0);
+ case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->level,(float*)arg,20.0);
+ case AF_CONTROL_VOLUME_PROBE | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->pow,(float*)arg,10.0);
+ case AF_CONTROL_VOLUME_PROBE_MAX | AF_CONTROL_GET:
+ return af_to_dB(AF_NCH,s->max,(float*)arg,10.0);
+ case AF_CONTROL_PRE_DESTROY:{
+ float m = 0.0;
+ int i;
+ if(!s->fast){
+ for(i=0;i<AF_NCH;i++)
+ m=max(m,s->max[i]);
+ af_to_dB(1, &m, &m, 10.0);
+ mp_msg(MSGT_AFILTER, MSGL_INFO, "[volume] The maximum volume was %0.2fdB \n", m);
+ }
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance* af)
+{
+ free(af->data);
+ free(af->setup);
+}
+
+// Filter data through filter
+static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
+{
+ struct mp_audio* c = data; // Current working data
+ af_volume_t* s = (af_volume_t*)af->setup; // Setup for this instance
+ register int nch = c->nch; // Number of channels
+ register int i = 0;
+
+ // Basic operation volume control only (used on slow machines)
+ if(af->data->format == (AF_FORMAT_S16_NE)){
+ int16_t* a = (int16_t*)c->audio; // Audio data
+ int len = c->len/2; // Number of samples
+ for (int ch = 0; ch < nch; ch++) {
+ int vol = 256.0 * s->level[ch];
+ if (s->enable[ch] && vol != 256) {
+ for(i=ch;i<len;i+=nch){
+ register int x = (a[i] * vol) >> 8;
+ a[i]=clamp(x,SHRT_MIN,SHRT_MAX);
+ }
+ }
+ }
+ }
+ // Machine is fast and data is floating point
+ else if(af->data->format == (AF_FORMAT_FLOAT_NE)){
+ float* a = (float*)c->audio; // Audio data
+ int len = c->len/4; // Number of samples
+ for (int ch = 0; ch < nch; ch++) {
+ // Volume control (fader)
+ if(s->enable[ch]){
+ float t = 1.0 - s->time;
+ for(i=ch;i<len;i+=nch){
+ register float x = a[i];
+ register float pow = x*x;
+ // Check maximum power value
+ if(pow > s->max[ch])
+ s->max[ch] = pow;
+ // Set volume
+ x *= s->level[ch];
+ // Peak meter
+ pow = x*x;
+ if(pow > s->pow[ch])
+ s->pow[ch] = pow;
+ else
+ s->pow[ch] = t*s->pow[ch] + pow*s->time; // LP filter
+ /* Soft clipping, the sound of a dream, thanks to Jon Wattes
+ post to Musicdsp.org */
+ if(s->soft)
+ x=af_softclip(x);
+ // Hard clipping
+ else
+ x=clamp(x,-1.0,1.0);
+ a[i] = x;
+ }
+ }
+ }
+ }
+ return c;
+}
+
+// Allocate memory and set function pointers
+static int af_open(struct af_instance* af){
+ int i = 0;
+ af->control=control;
+ af->uninit=uninit;
+ af->play=play;
+ af->mul=1;
+ af->data=calloc(1,sizeof(struct mp_audio));
+ af->setup=calloc(1,sizeof(af_volume_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+ // Enable volume control and set initial volume to 0dB.
+ for(i=0;i<AF_NCH;i++){
+ ((af_volume_t*)af->setup)->enable[i] = 1;
+ ((af_volume_t*)af->setup)->level[i] = 1.0;
+ }
+ return AF_OK;
+}
+
+// Description of this filter
+struct af_info af_info_volume = {
+ "Volume control audio filter",
+ "volume",
+ "Anders",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
diff --git a/audio/filter/control.h b/audio/filter/control.h
new file mode 100644
index 0000000000..323b9a3924
--- /dev/null
+++ b/audio/filter/control.h
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_CONTROL_H
+#define MPLAYER_CONTROL_H
+
+#include <sys/types.h>
+
+/*********************************************
+// Control info struct.
+//
+// This struct is the argument in a info call to a filter.
+*/
+
+// Argument types
+#define AF_CONTROL_TYPE_BOOL (0x0<<0)
+#define AF_CONTROL_TYPE_CHAR (0x1<<0)
+#define AF_CONTROL_TYPE_INT (0x2<<0)
+#define AF_CONTROL_TYPE_FLOAT (0x3<<0)
+#define AF_CONTROL_TYPE_STRUCT (0x4<<0)
+#define AF_CONTROL_TYPE_SPECIAL (0x5<<0) // a pointer to a function for example
+#define AF_CONTROL_TYPE_MASK (0x7<<0)
+// Argument geometry
+#define AF_CONTROL_GEOM_SCALAR (0x0<<3)
+#define AF_CONTROL_GEOM_ARRAY (0x1<<3)
+#define AF_CONTROL_GEOM_MATRIX (0x2<<3)
+#define AF_CONTROL_GEOM_MASK (0x3<<3)
+// Argument properties
+#define AF_CONTROL_PROP_READ (0x0<<5) // The argument can be read
+#define AF_CONTROL_PROP_WRITE (0x1<<5) // The argument can be written
+#define AF_CONTROL_PROP_SAVE (0x2<<5) // Can be saved
+#define AF_CONTROL_PROP_RUNTIME (0x4<<5) // Acessable during execution
+#define AF_CONTROL_PROP_CHANNEL (0x8<<5) // Argument is set per channel
+#define AF_CONTROL_PROP_MASK (0xF<<5)
+
+typedef struct af_control_info_s{
+ int def; // Control enumrification
+ char* name; // Name of argument
+ char* info; // Description of what it does
+ int flags; // Flags as defined above
+ float max; // Max and min value
+ float min; // (only aplicable on float and int)
+ int xdim; // 1st dimension
+ int ydim; // 2nd dimension (=0 for everything except matrix)
+ size_t sz; // Size of argument in bytes
+ int ch; // Channel number (for future use)
+ void* arg; // Data (for future use)
+}af_control_info_t;
+
+
+/*********************************************
+// Extended control used with arguments that operates on only one
+// channel at the time
+*/
+typedef struct af_control_ext_s{
+ void* arg; // Argument
+ int ch; // Chanel number
+}af_control_ext_t;
+
+/*********************************************
+// Control parameters
+*/
+
+/* The control system is divided into 3 levels
+ mandatory calls - all filters must answer to all of these
+ optional calls - are optional
+ filter specific calls - applies only to some filters
+*/
+
+#define AF_CONTROL_MANDATORY 0x10000000
+#define AF_CONTROL_OPTIONAL 0x20000000
+#define AF_CONTROL_FILTER_SPECIFIC 0x40000000
+
+// MANDATORY CALLS
+
+/* Reinitialize filter. The optional argument contains the new
+ configuration in form of a struct mp_audio struct. If the filter does not
+ support the new format the struct should be changed and AF_FALSE
+ should be returned. If the incoming and outgoing data streams are
+ identical the filter can return AF_DETACH. This will remove the
+ filter. */
+#define AF_CONTROL_REINIT 0x00000100 | AF_CONTROL_MANDATORY
+
+// OPTIONAL CALLS
+
+/* Called just after creation with the af_cfg for the stream in which
+ the filter resides as input parameter this call can be used by the
+ filter to initialize itself */
+#define AF_CONTROL_POST_CREATE 0x00000100 | AF_CONTROL_OPTIONAL
+
+// Called just before destruction of a filter
+#define AF_CONTROL_PRE_DESTROY 0x00000200 | AF_CONTROL_OPTIONAL
+
+/* Commandline parameters. If there were any commandline parameters
+ for this specific filter, they will be given as a char* in the
+ argument */
+#define AF_CONTROL_COMMAND_LINE 0x00000300 | AF_CONTROL_OPTIONAL
+
+
+// FILTER SPECIFIC CALLS
+
+// Basic operations: These can be ored with any of the below calls
+// Set argument
+#define AF_CONTROL_SET 0x00000000
+// Get argument
+#define AF_CONTROL_GET 0x00000001
+// Get info about the control, i.e fill in everything except argument
+#define AF_CONTROL_INFO 0x00000002
+
+// Resample
+
+// Set output rate in resample
+#define AF_CONTROL_RESAMPLE_RATE 0x00000100 | AF_CONTROL_FILTER_SPECIFIC
+
+// Enable sloppy resampling
+#define AF_CONTROL_RESAMPLE_SLOPPY 0x00000200 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set resampling accuracy
+#define AF_CONTROL_RESAMPLE_ACCURACY 0x00000300 | AF_CONTROL_FILTER_SPECIFIC
+
+// Format
+
+#define AF_CONTROL_FORMAT_FMT 0x00000400 | AF_CONTROL_FILTER_SPECIFIC
+
+// Channels
+
+// Set number of output channels in channels
+#define AF_CONTROL_CHANNELS 0x00000600 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set number of channel routes
+#define AF_CONTROL_CHANNELS_ROUTES 0x00000700 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set channel routing pair, arg is int[2] and ch is used
+#define AF_CONTROL_CHANNELS_ROUTING 0x00000800 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set nuber of channel routing pairs, arg is int*
+#define AF_CONTROL_CHANNELS_NR 0x00000900 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set make af_channels into a router
+#define AF_CONTROL_CHANNELS_ROUTER 0x00000A00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Volume
+
+// Turn volume control on and off, arg is int*
+#define AF_CONTROL_VOLUME_ON_OFF 0x00000B00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Turn soft clipping of the volume on and off, arg is binary
+#define AF_CONTROL_VOLUME_SOFTCLIP 0x00000C00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set volume level, arg is a float* with the volume for all the channels
+#define AF_CONTROL_VOLUME_LEVEL 0x00000D00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Probed power level for all channels, arg is a float*
+#define AF_CONTROL_VOLUME_PROBE 0x00000E00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Maximum probed power level for all channels, arg is a float*
+#define AF_CONTROL_VOLUME_PROBE_MAX 0x00000F00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compressor/expander
+
+// Turn compressor/expander on and off
+#define AF_CONTROL_COMP_ON_OFF 0x00001000 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion threshold [dB]
+#define AF_CONTROL_COMP_THRESH 0x00001100 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion attack time [ms]
+#define AF_CONTROL_COMP_ATTACK 0x00001200 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion release time [ms]
+#define AF_CONTROL_COMP_RELEASE 0x00001300 | AF_CONTROL_FILTER_SPECIFIC
+
+// Compression/expansion gain level [dB]
+#define AF_CONTROL_COMP_RATIO 0x00001400 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate
+
+// Turn noise gate on an off
+#define AF_CONTROL_GATE_ON_OFF 0x00001500 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate threshold [dB]
+#define AF_CONTROL_GATE_THRESH 0x00001600 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate attack time [ms]
+#define AF_CONTROL_GATE_ATTACK 0x00001700 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate release time [ms]
+#define AF_CONTROL_GATE_RELEASE 0x00001800 | AF_CONTROL_FILTER_SPECIFIC
+
+// Noise gate release range level [dB]
+#define AF_CONTROL_GATE_RANGE 0x00001900 | AF_CONTROL_FILTER_SPECIFIC
+
+// Pan
+
+// Pan levels, arg is a control_ext with a float*
+#define AF_CONTROL_PAN_LEVEL 0x00001A00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Number of outputs from pan, arg is int*
+#define AF_CONTROL_PAN_NOUT 0x00001B00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Balance, arg is float*; range -1 (left) to 1 (right), 0 center
+#define AF_CONTROL_PAN_BALANCE 0x00001C00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Set equalizer gain, arg is a control_ext with a float*
+#define AF_CONTROL_EQUALIZER_GAIN 0x00001D00 | AF_CONTROL_FILTER_SPECIFIC
+
+
+// Delay length in ms, arg is a control_ext with a float*
+#define AF_CONTROL_DELAY_LEN 0x00001E00 | AF_CONTROL_FILTER_SPECIFIC
+
+
+// Subwoofer
+
+// Channel number which to insert the filtered data, arg in int*
+#define AF_CONTROL_SUB_CH 0x00001F00 | AF_CONTROL_FILTER_SPECIFIC
+
+// Cutoff frequency [Hz] for lowpass filter, arg is float*
+#define AF_CONTROL_SUB_FC 0x00002000 | AF_CONTROL_FILTER_SPECIFIC
+
+
+// Export
+#define AF_CONTROL_EXPORT_SZ 0x00003000 | AF_CONTROL_FILTER_SPECIFIC
+
+
+// ExtraStereo Multiplier
+#define AF_CONTROL_ES_MUL 0x00003100 | AF_CONTROL_FILTER_SPECIFIC
+
+
+// Center
+
+// Channel number which to inster the filtered data, arg in int*
+#define AF_CONTROL_CENTER_CH 0x00003200 | AF_CONTROL_FILTER_SPECIFIC
+
+
+// SineSuppress
+#define AF_CONTROL_SS_FREQ 0x00003300 | AF_CONTROL_FILTER_SPECIFIC
+#define AF_CONTROL_SS_DECAY 0x00003400 | AF_CONTROL_FILTER_SPECIFIC
+
+#define AF_CONTROL_PLAYBACK_SPEED 0x00003500 | AF_CONTROL_FILTER_SPECIFIC
+#define AF_CONTROL_SCALETEMPO_AMOUNT 0x00003600 | AF_CONTROL_FILTER_SPECIFIC
+
+#endif /* MPLAYER_CONTROL_H */
diff --git a/audio/filter/dsp.h b/audio/filter/dsp.h
new file mode 100644
index 0000000000..561b86cfe0
--- /dev/null
+++ b/audio/filter/dsp.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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.
+ */
+
+#ifndef MPLAYER_DSP_H
+#define MPLAYER_DSP_H
+
+/* Implementation of routines used for DSP */
+
+/* Size of floating point type used in routines */
+#define FLOAT_TYPE float
+
+#include "window.h"
+#include "filter.h"
+
+#endif /* MPLAYER_DSP_H */
diff --git a/audio/filter/equalizer.h b/audio/filter/equalizer.h
new file mode 100644
index 0000000000..4935401413
--- /dev/null
+++ b/audio/filter/equalizer.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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.
+ */
+
+#ifndef MPLAYER_EQUALIZER_H
+#define MPLAYER_EQUALIZER_H
+
+/* Equalizer plugin header file defines struct used for setting or
+ getting the gain of a specific channel and frequency */
+
+typedef struct equalizer_s
+{
+ float gain; // Gain in dB -15 - 15
+ int channel; // Channel number 0 - 5
+ int band; // Frequency band 0 - 9
+}equalizer_t;
+
+/* The different frequency bands are:
+nr. center frequency
+0 31.25 Hz
+1 62.50 Hz
+2 125.0 Hz
+3 250.0 Hz
+4 500.0 Hz
+5 1.000 kHz
+6 2.000 kHz
+7 4.000 kHz
+8 8.000 kHz
+9 16.00 kHz
+*/
+
+#endif /* MPLAYER_EQUALIZER_H */
diff --git a/audio/filter/filter.c b/audio/filter/filter.c
new file mode 100644
index 0000000000..b272125fd8
--- /dev/null
+++ b/audio/filter/filter.c
@@ -0,0 +1,360 @@
+/*
+ * design and implementation of different types of digital filters
+ *
+ * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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 <string.h>
+#include <math.h>
+#include "dsp.h"
+
+/******************************************************************************
+* FIR filter implementations
+******************************************************************************/
+
+/* C implementation of FIR filter y=w*x
+
+ n number of filter taps, where mod(n,4)==0
+ w filter taps
+ x input signal must be a circular buffer which is indexed backwards
+*/
+inline FLOAT_TYPE af_filter_fir(register unsigned int n, const FLOAT_TYPE* w,
+ const FLOAT_TYPE* x)
+{
+ register FLOAT_TYPE y; // Output
+ y = 0.0;
+ do{
+ n--;
+ y+=w[n]*x[n];
+ }while(n != 0);
+ return y;
+}
+
+/******************************************************************************
+* FIR filter design
+******************************************************************************/
+
+/* Design FIR filter using the Window method
+
+ n filter length must be odd for HP and BS filters
+ w buffer for the filter taps (must be n long)
+ fc cutoff frequencies (1 for LP and HP, 2 for BP and BS)
+ 0 < fc < 1 where 1 <=> Fs/2
+ flags window and filter type as defined in filter.h
+ variables are ored together: i.e. LP|HAMMING will give a
+ low pass filter designed using a hamming window
+ opt beta constant used only when designing using kaiser windows
+
+ returns 0 if OK, -1 if fail
+*/
+int af_filter_design_fir(unsigned int n, FLOAT_TYPE* w, const FLOAT_TYPE* fc,
+ unsigned int flags, FLOAT_TYPE opt)
+{
+ unsigned int o = n & 1; // Indicator for odd filter length
+ unsigned int end = ((n + 1) >> 1) - o; // Loop end
+ unsigned int i; // Loop index
+
+ FLOAT_TYPE k1 = 2 * M_PI; // 2*pi*fc1
+ FLOAT_TYPE k2 = 0.5 * (FLOAT_TYPE)(1 - o);// Constant used if the filter has even length
+ FLOAT_TYPE k3; // 2*pi*fc2 Constant used in BP and BS design
+ FLOAT_TYPE g = 0.0; // Gain
+ FLOAT_TYPE t1,t2,t3; // Temporary variables
+ FLOAT_TYPE fc1,fc2; // Cutoff frequencies
+
+ // Sanity check
+ if(!w || (n == 0)) return -1;
+
+ // Get window coefficients
+ switch(flags & WINDOW_MASK){
+ case(BOXCAR):
+ af_window_boxcar(n,w); break;
+ case(TRIANG):
+ af_window_triang(n,w); break;
+ case(HAMMING):
+ af_window_hamming(n,w); break;
+ case(HANNING):
+ af_window_hanning(n,w); break;
+ case(BLACKMAN):
+ af_window_blackman(n,w); break;
+ case(FLATTOP):
+ af_window_flattop(n,w); break;
+ case(KAISER):
+ af_window_kaiser(n,w,opt); break;
+ default:
+ return -1;
+ }
+
+ if(flags & (LP | HP)){
+ fc1=*fc;
+ // Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
+ fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1/2 : 0.25;
+ k1 *= fc1;
+
+ if(flags & LP){ // Low pass filter
+
+ // If the filter length is odd, there is one point which is exactly
+ // in the middle. The value at this point is 2*fCutoff*sin(x)/x,
+ // where x is zero. To make sure nothing strange happens, we set this
+ // value separately.
+ if (o){
+ w[end] = fc1 * w[end] * 2.0;
+ g=w[end];
+ }
+
+ // Create filter
+ for (i=0 ; i<end ; i++){
+ t1 = (FLOAT_TYPE)(i+1) - k2;
+ w[end-i-1] = w[n-end+i] = w[end-i-1] * sin(k1 * t1)/(M_PI * t1); // Sinc
+ g += 2*w[end-i-1]; // Total gain in filter
+ }
+ }
+ else{ // High pass filter
+ if (!o) // High pass filters must have odd length
+ return -1;
+ w[end] = 1.0 - (fc1 * w[end] * 2.0);
+ g= w[end];
+
+ // Create filter
+ for (i=0 ; i<end ; i++){
+ t1 = (FLOAT_TYPE)(i+1);
+ w[end-i-1] = w[n-end+i] = -1 * w[end-i-1] * sin(k1 * t1)/(M_PI * t1); // Sinc
+ g += ((i&1) ? (2*w[end-i-1]) : (-2*w[end-i-1])); // Total gain in filter
+ }
+ }
+ }
+
+ if(flags & (BP | BS)){
+ fc1=fc[0];
+ fc2=fc[1];
+ // Cutoff frequencies must be < 1.0 where 1.0 <=> Fs/2
+ fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1/2 : 0.25;
+ fc2 = ((fc2 <= 1.0) && (fc2 > 0.0)) ? fc2/2 : 0.25;
+ k3 = k1 * fc2; // 2*pi*fc2
+ k1 *= fc1; // 2*pi*fc1
+
+ if(flags & BP){ // Band pass
+ // Calculate center tap
+ if (o){
+ g=w[end]*(fc1+fc2);
+ w[end] = (fc2 - fc1) * w[end] * 2.0;
+ }
+
+ // Create filter
+ for (i=0 ; i<end ; i++){
+ t1 = (FLOAT_TYPE)(i+1) - k2;
+ t2 = sin(k3 * t1)/(M_PI * t1); // Sinc fc2
+ t3 = sin(k1 * t1)/(M_PI * t1); // Sinc fc1
+ g += w[end-i-1] * (t3 + t2); // Total gain in filter
+ w[end-i-1] = w[n-end+i] = w[end-i-1] * (t2 - t3);
+ }
+ }
+ else{ // Band stop
+ if (!o) // Band stop filters must have odd length
+ return -1;
+ w[end] = 1.0 - (fc2 - fc1) * w[end] * 2.0;
+ g= w[end];
+
+ // Create filter
+ for (i=0 ; i<end ; i++){
+ t1 = (FLOAT_TYPE)(i+1);
+ t2 = sin(k1 * t1)/(M_PI * t1); // Sinc fc1
+ t3 = sin(k3 * t1)/(M_PI * t1); // Sinc fc2
+ w[end-i-1] = w[n-end+i] = w[end-i-1] * (t2 - t3);
+ g += 2*w[end-i-1]; // Total gain in filter
+ }
+ }
+ }
+
+ // Normalize gain
+ g=1/g;
+ for (i=0; i<n; i++)
+ w[i] *= g;
+
+ return 0;
+}
+
+/******************************************************************************
+* IIR filter design
+******************************************************************************/
+
+/* Helper functions for the bilinear transform */
+
+/* Pre-warp the coefficients of a numerator or denominator.
+ Note that a0 is assumed to be 1, so there is no wrapping
+ of it.
+*/
+static void af_filter_prewarp(FLOAT_TYPE* a, FLOAT_TYPE fc, FLOAT_TYPE fs)
+{
+ FLOAT_TYPE wp;
+ wp = 2.0 * fs * tan(M_PI * fc / fs);
+ a[2] = a[2]/(wp * wp);
+ a[1] = a[1]/wp;
+}
+
+/* Transform the numerator and denominator coefficients of s-domain
+ biquad section into corresponding z-domain coefficients.
+
+ The transfer function for z-domain is:
+
+ 1 + alpha1 * z^(-1) + alpha2 * z^(-2)
+ H(z) = -------------------------------------
+ 1 + beta1 * z^(-1) + beta2 * z^(-2)
+
+ Store the 4 IIR coefficients in array pointed by coef in following
+ order:
+ beta1, beta2 (denominator)
+ alpha1, alpha2 (numerator)
+
+ Arguments:
+ a - s-domain numerator coefficients
+ b - s-domain denominator coefficients
+ k - filter gain factor. Initially set to 1 and modified by each
+ biquad section in such a way, as to make it the
+ coefficient by which to multiply the overall filter gain
+ in order to achieve a desired overall filter gain,
+ specified in initial value of k.
+ fs - sampling rate (Hz)
+ coef - array of z-domain coefficients to be filled in.
+
+ Return: On return, set coef z-domain coefficients and k to the gain
+ required to maintain overall gain = 1.0;
+*/
+static void af_filter_bilinear(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE* k,
+ FLOAT_TYPE fs, FLOAT_TYPE *coef)
+{
+ FLOAT_TYPE ad, bd;
+
+ /* alpha (Numerator in s-domain) */
+ ad = 4. * a[2] * fs * fs + 2. * a[1] * fs + a[0];
+ /* beta (Denominator in s-domain) */
+ bd = 4. * b[2] * fs * fs + 2. * b[1] * fs + b[0];
+
+ /* Update gain constant for this section */
+ *k *= ad/bd;
+
+ /* Denominator */
+ *coef++ = (2. * b[0] - 8. * b[2] * fs * fs)/bd; /* beta1 */
+ *coef++ = (4. * b[2] * fs * fs - 2. * b[1] * fs + b[0])/bd; /* beta2 */
+
+ /* Numerator */
+ *coef++ = (2. * a[0] - 8. * a[2] * fs * fs)/ad; /* alpha1 */
+ *coef = (4. * a[2] * fs * fs - 2. * a[1] * fs + a[0])/ad; /* alpha2 */
+}
+
+
+
+/* IIR filter design using bilinear transform and prewarp. Transforms
+ 2nd order s domain analog filter into a digital IIR biquad link. To
+ create a filter fill in a, b, Q and fs and make space for coef and k.
+
+
+ Example Butterworth design:
+
+ Below are Butterworth polynomials, arranged as a series of 2nd
+ order sections:
+
+ Note: n is filter order.
+
+ n Polynomials
+ -------------------------------------------------------------------
+ 2 s^2 + 1.4142s + 1
+ 4 (s^2 + 0.765367s + 1) * (s^2 + 1.847759s + 1)
+ 6 (s^2 + 0.5176387s + 1) * (s^2 + 1.414214 + 1) * (s^2 + 1.931852s + 1)
+
+ For n=4 we have following equation for the filter transfer function:
+ 1 1
+ T(s) = --------------------------- * ----------------------------
+ s^2 + (1/Q) * 0.765367s + 1 s^2 + (1/Q) * 1.847759s + 1
+
+ The filter consists of two 2nd order sections since highest s power
+ is 2. Now we can take the coefficients, or the numbers by which s
+ is multiplied and plug them into a standard formula to be used by
+ bilinear transform.
+
+ Our standard form for each 2nd order section is:
+
+ a2 * s^2 + a1 * s + a0
+ H(s) = ----------------------
+ b2 * s^2 + b1 * s + b0
+
+ Note that Butterworth numerator is 1 for all filter sections, which
+ means s^2 = 0 and s^1 = 0
+
+ Let's convert standard Butterworth polynomials into this form:
+
+ 0 + 0 + 1 0 + 0 + 1
+ --------------------------- * --------------------------
+ 1 + ((1/Q) * 0.765367) + 1 1 + ((1/Q) * 1.847759) + 1
+
+ Section 1:
+ a2 = 0; a1 = 0; a0 = 1;
+ b2 = 1; b1 = 0.765367; b0 = 1;
+
+ Section 2:
+ a2 = 0; a1 = 0; a0 = 1;
+ b2 = 1; b1 = 1.847759; b0 = 1;
+
+ Q is filter quality factor or resonance, in the range of 1 to
+ 1000. The overall filter Q is a product of all 2nd order stages.
+ For example, the 6th order filter (3 stages, or biquads) with
+ individual Q of 2 will have filter Q = 2 * 2 * 2 = 8.
+
+
+ Arguments:
+ a - s-domain numerator coefficients, a[1] is always assumed to be 1.0
+ b - s-domain denominator coefficients
+ Q - Q value for the filter
+ k - filter gain factor. Initially set to 1 and modified by each
+ biquad section in such a way, as to make it the
+ coefficient by which to multiply the overall filter gain
+ in order to achieve a desired overall filter gain,
+ specified in initial value of k.
+ fs - sampling rate (Hz)
+ coef - array of z-domain coefficients to be filled in.
+
+ Note: Upon return from each call, the k argument will be set to a
+ value, by which to multiply our actual signal in order for the gain
+ to be one. On second call to szxform() we provide k that was
+ changed by the previous section. During actual audio filtering
+ k can be used for gain compensation.
+
+ return -1 if fail 0 if success.
+*/
+int af_filter_szxform(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE Q, FLOAT_TYPE fc,
+ FLOAT_TYPE fs, FLOAT_TYPE *k, FLOAT_TYPE *coef)
+{
+ FLOAT_TYPE at[3];
+ FLOAT_TYPE bt[3];
+
+ if(!a || !b || !k || !coef || (Q>1000.0 || Q< 1.0))
+ return -1;
+
+ memcpy(at,a,3*sizeof(FLOAT_TYPE));
+ memcpy(bt,b,3*sizeof(FLOAT_TYPE));
+
+ bt[1]/=Q;
+
+ /* Calculate a and b and overwrite the original values */
+ af_filter_prewarp(at, fc, fs);
+ af_filter_prewarp(bt, fc, fs);
+ /* Execute bilinear transform */
+ af_filter_bilinear(at, bt, k, fs, coef);
+
+ return 0;
+}
diff --git a/audio/filter/filter.h b/audio/filter/filter.h
new file mode 100644
index 0000000000..aed33352c2
--- /dev/null
+++ b/audio/filter/filter.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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.
+ */
+
+#if !defined MPLAYER_DSP_H
+# error Never use filter.h directly; include dsp.h instead.
+#endif
+
+#ifndef MPLAYER_FILTER_H
+#define MPLAYER_FILTER_H
+
+
+// Design and implementation of different types of digital filters
+
+
+// Flags used for filter design
+
+// Filter characteristics
+#define LP 0x00010000 // Low pass
+#define HP 0x00020000 // High pass
+#define BP 0x00040000 // Band pass
+#define BS 0x00080000 // Band stop
+#define TYPE_MASK 0x000F0000
+
+// Window types
+#define BOXCAR 0x00000001
+#define TRIANG 0x00000002
+#define HAMMING 0x00000004
+#define HANNING 0x00000008
+#define BLACKMAN 0x00000010
+#define FLATTOP 0x00000011
+#define KAISER 0x00000012
+#define WINDOW_MASK 0x0000001F
+
+// Parallel filter design
+#define FWD 0x00000001 // Forward indexing of polyphase filter
+#define REW 0x00000002 // Reverse indexing of polyphase filter
+#define ODD 0x00000010 // Make filter HP
+
+// Exported functions
+FLOAT_TYPE af_filter_fir(unsigned int n, const FLOAT_TYPE* w, const FLOAT_TYPE* x);
+
+int af_filter_design_fir(unsigned int n, FLOAT_TYPE* w, const FLOAT_TYPE* fc,
+ unsigned int flags, FLOAT_TYPE opt);
+
+int af_filter_szxform(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE Q,
+ FLOAT_TYPE fc, FLOAT_TYPE fs, FLOAT_TYPE *k,
+ FLOAT_TYPE *coef);
+
+/* Add new data to circular queue designed to be used with a FIR
+ filter. xq is the circular queue, in pointing at the new sample, xi
+ current index for xq and n the length of the filter. xq must be n*2
+ long.
+*/
+#define af_filter_updateq(n,xi,xq,in)\
+ xq[xi]=(xq)[(xi)+(n)]=*(in);\
+ xi=(++(xi))&((n)-1);
+
+#endif /* MPLAYER_FILTER_H */
diff --git a/audio/filter/window.c b/audio/filter/window.c
new file mode 100644
index 0000000000..a970bdcbea
--- /dev/null
+++ b/audio/filter/window.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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.
+ */
+
+/* Calculates a number of window functions. The following window
+ functions are currently implemented: Boxcar, Triang, Hanning,
+ Hamming, Blackman, Flattop and Kaiser. In the function call n is
+ the number of filter taps and w the buffer in which the filter
+ coefficients will be stored.
+*/
+
+#include <math.h>
+#include "dsp.h"
+
+/*
+// Boxcar
+//
+// n window length
+// w buffer for the window parameters
+*/
+void af_window_boxcar(int n, FLOAT_TYPE* w)
+{
+ int i;
+ // Calculate window coefficients
+ for (i=0 ; i<n ; i++)
+ w[i] = 1.0;
+}
+
+
+/*
+// Triang a.k.a Bartlett
+//
+// | (N-1)|
+// 2 * |k - -----|
+// | 2 |
+// w = 1.0 - ---------------
+// N+1
+// n window length
+// w buffer for the window parameters
+*/
+void af_window_triang(int n, FLOAT_TYPE* w)
+{
+ FLOAT_TYPE k1 = (FLOAT_TYPE)(n & 1);
+ FLOAT_TYPE k2 = 1/((FLOAT_TYPE)n + k1);
+ int end = (n + 1) >> 1;
+ int i;
+
+ // Calculate window coefficients
+ for (i=0 ; i<end ; i++)
+ w[i] = w[n-i-1] = (2.0*((FLOAT_TYPE)(i+1))-(1.0-k1))*k2;
+}
+
+
+/*
+// Hanning
+// 2*pi*k
+// w = 0.5 - 0.5*cos(------), where 0 < k <= N
+// N+1
+// n window length
+// w buffer for the window parameters
+*/
+void af_window_hanning(int n, FLOAT_TYPE* w)
+{
+ int i;
+ FLOAT_TYPE k = 2*M_PI/((FLOAT_TYPE)(n+1)); // 2*pi/(N+1)
+
+ // Calculate window coefficients
+ for (i=0; i<n; i++)
+ *w++ = 0.5*(1.0 - cos(k*(FLOAT_TYPE)(i+1)));
+}
+
+/*
+// Hamming
+// 2*pi*k
+// w(k) = 0.54 - 0.46*cos(------), where 0 <= k < N
+// N-1
+//
+// n window length
+// w buffer for the window parameters
+*/
+void af_window_hamming(int n,FLOAT_TYPE* w)
+{
+ int i;
+ FLOAT_TYPE k = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1)
+
+ // Calculate window coefficients
+ for (i=0; i<n; i++)
+ *w++ = 0.54 - 0.46*cos(k*(FLOAT_TYPE)i);
+}
+
+/*
+// Blackman
+// 2*pi*k 4*pi*k
+// w(k) = 0.42 - 0.5*cos(------) + 0.08*cos(------), where 0 <= k < N
+// N-1 N-1
+//
+// n window length
+// w buffer for the window parameters
+*/
+void af_window_blackman(int n,FLOAT_TYPE* w)
+{
+ int i;
+ FLOAT_TYPE k1 = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1)
+ FLOAT_TYPE k2 = 2*k1; // 4*pi/(N-1)
+
+ // Calculate window coefficients
+ for (i=0; i<n; i++)
+ *w++ = 0.42 - 0.50*cos(k1*(FLOAT_TYPE)i) + 0.08*cos(k2*(FLOAT_TYPE)i);
+}
+
+/*
+// Flattop
+// 2*pi*k 4*pi*k
+// w(k) = 0.2810638602 - 0.5208971735*cos(------) + 0.1980389663*cos(------), where 0 <= k < N
+// N-1 N-1
+//
+// n window length
+// w buffer for the window parameters
+*/
+void af_window_flattop(int n,FLOAT_TYPE* w)
+{
+ int i;
+ FLOAT_TYPE k1 = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1)
+ FLOAT_TYPE k2 = 2*k1; // 4*pi/(N-1)
+
+ // Calculate window coefficients
+ for (i=0; i<n; i++)
+ *w++ = 0.2810638602 - 0.5208971735*cos(k1*(FLOAT_TYPE)i)
+ + 0.1980389663*cos(k2*(FLOAT_TYPE)i);
+}
+
+/* Computes the 0th order modified Bessel function of the first kind.
+// (Needed to compute Kaiser window)
+//
+// y = sum( (x/(2*n))^2 )
+// n
+*/
+#define BIZ_EPSILON 1E-21 // Max error acceptable
+
+static FLOAT_TYPE besselizero(FLOAT_TYPE x)
+{
+ FLOAT_TYPE temp;
+ FLOAT_TYPE sum = 1.0;
+ FLOAT_TYPE u = 1.0;
+ FLOAT_TYPE halfx = x/2.0;
+ int n = 1;
+
+ do {
+ temp = halfx/(FLOAT_TYPE)n;
+ u *=temp * temp;
+ sum += u;
+ n++;
+ } while (u >= BIZ_EPSILON * sum);
+ return sum;
+}
+
+/*
+// Kaiser
+//
+// n window length
+// w buffer for the window parameters
+// b beta parameter of Kaiser window, Beta >= 1
+//
+// Beta trades the rejection of the low pass filter against the
+// transition width from passband to stop band. Larger Beta means a
+// slower transition and greater stop band rejection. See Rabiner and
+// Gold (Theory and Application of DSP) under Kaiser windows for more
+// about Beta. The following table from Rabiner and Gold gives some
+// feel for the effect of Beta:
+//
+// All ripples in dB, width of transition band = D*N where N = window
+// length
+//
+// BETA D PB RIP SB RIP
+// 2.120 1.50 +-0.27 -30
+// 3.384 2.23 0.0864 -40
+// 4.538 2.93 0.0274 -50
+// 5.658 3.62 0.00868 -60
+// 6.764 4.32 0.00275 -70
+// 7.865 5.0 0.000868 -80
+// 8.960 5.7 0.000275 -90
+// 10.056 6.4 0.000087 -100
+*/
+void af_window_kaiser(int n, FLOAT_TYPE* w, FLOAT_TYPE b)
+{
+ FLOAT_TYPE tmp;
+ FLOAT_TYPE k1 = 1.0/besselizero(b);
+ int k2 = 1 - (n & 1);
+ int end = (n + 1) >> 1;
+ int i;
+
+ // Calculate window coefficients
+ for (i=0 ; i<end ; i++){
+ tmp = (FLOAT_TYPE)(2*i + k2) / ((FLOAT_TYPE)n - 1.0);
+ w[end-(1&(!k2))+i] = w[end-1-i] = k1 * besselizero(b*sqrt(1.0 - tmp*tmp));
+ }
+}
diff --git a/audio/filter/window.h b/audio/filter/window.h
new file mode 100644
index 0000000000..1c179b7902
--- /dev/null
+++ b/audio/filter/window.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
+ *
+ * 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.
+ */
+
+/* Calculates a number of window functions. The following window
+ functions are currently implemented: Boxcar, Triang, Hanning,
+ Hamming, Blackman, Flattop and Kaiser. In the function call n is
+ the number of filter taps and w the buffer in which the filter
+ coefficients will be stored.
+*/
+
+#if !defined MPLAYER_DSP_H
+# error Never use window.h directly; include dsp.h instead.
+#endif
+
+#ifndef MPLAYER_WINDOW_H
+#define MPLAYER_WINDOW_H
+
+void af_window_boxcar(int n, FLOAT_TYPE* w);
+void af_window_triang(int n, FLOAT_TYPE* w);
+void af_window_hanning(int n, FLOAT_TYPE* w);
+void af_window_hamming(int n, FLOAT_TYPE* w);
+void af_window_blackman(int n, FLOAT_TYPE* w);
+void af_window_flattop(int n, FLOAT_TYPE* w);
+void af_window_kaiser(int n, FLOAT_TYPE* w, FLOAT_TYPE b);
+
+#endif /* MPLAYER_WINDOW_H */
diff --git a/audio/format.c b/audio/format.c
new file mode 100644
index 0000000000..88d66522a0
--- /dev/null
+++ b/audio/format.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2005 Alex Beregszaszi
+ *
+ * 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 <string.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include "af.h"
+
+int af_fmt2bits(int format)
+{
+ if (AF_FORMAT_IS_AC3(format)) return 16;
+ return (format & AF_FORMAT_BITS_MASK)+8;
+// return (((format & AF_FORMAT_BITS_MASK)>>3)+1) * 8;
+#if 0
+ switch(format & AF_FORMAT_BITS_MASK)
+ {
+ case AF_FORMAT_8BIT: return 8;
+ case AF_FORMAT_16BIT: return 16;
+ case AF_FORMAT_24BIT: return 24;
+ case AF_FORMAT_32BIT: return 32;
+ case AF_FORMAT_48BIT: return 48;
+ }
+#endif
+ return -1;
+}
+
+int af_bits2fmt(int bits)
+{
+ return (bits/8 - 1) << 3;
+}
+
+/* Convert format to str input str is a buffer for the
+ converted string, size is the size of the buffer */
+char* af_fmt2str(int format, char* str, int size)
+{
+ const char *name = af_fmt2str_short(format);
+ if (name) {
+ snprintf(str, size, "%s", name);
+ } else {
+ snprintf(str, size, "%#x", format);
+ }
+ return str;
+}
+
+const struct af_fmt_entry af_fmtstr_table[] = {
+ { "mulaw", AF_FORMAT_MU_LAW },
+ { "alaw", AF_FORMAT_A_LAW },
+ { "mpeg2", AF_FORMAT_MPEG2 },
+ { "ac3le", AF_FORMAT_AC3_LE },
+ { "ac3be", AF_FORMAT_AC3_BE },
+ { "ac3ne", AF_FORMAT_AC3_NE },
+ { "iec61937le", AF_FORMAT_IEC61937_LE },
+ { "iec61937be", AF_FORMAT_IEC61937_BE },
+ { "iec61937ne", AF_FORMAT_IEC61937_NE },
+ { "imaadpcm", AF_FORMAT_IMA_ADPCM },
+
+ { "u8", AF_FORMAT_U8 },
+ { "s8", AF_FORMAT_S8 },
+ { "u16le", AF_FORMAT_U16_LE },
+ { "u16be", AF_FORMAT_U16_BE },
+ { "u16ne", AF_FORMAT_U16_NE },
+ { "s16le", AF_FORMAT_S16_LE },
+ { "s16be", AF_FORMAT_S16_BE },
+ { "s16ne", AF_FORMAT_S16_NE },
+ { "u24le", AF_FORMAT_U24_LE },
+ { "u24be", AF_FORMAT_U24_BE },
+ { "u24ne", AF_FORMAT_U24_NE },
+ { "s24le", AF_FORMAT_S24_LE },
+ { "s24be", AF_FORMAT_S24_BE },
+ { "s24ne", AF_FORMAT_S24_NE },
+ { "u32le", AF_FORMAT_U32_LE },
+ { "u32be", AF_FORMAT_U32_BE },
+ { "u32ne", AF_FORMAT_U32_NE },
+ { "s32le", AF_FORMAT_S32_LE },
+ { "s32be", AF_FORMAT_S32_BE },
+ { "s32ne", AF_FORMAT_S32_NE },
+ { "floatle", AF_FORMAT_FLOAT_LE },
+ { "floatbe", AF_FORMAT_FLOAT_BE },
+ { "floatne", AF_FORMAT_FLOAT_NE },
+
+ {0}
+};
+
+const char *af_fmt2str_short(int format)
+{
+ int i;
+
+ for (i = 0; af_fmtstr_table[i].name; i++)
+ if (af_fmtstr_table[i].format == format)
+ return af_fmtstr_table[i].name;
+
+ return "??";
+}
+
+static bool af_fmt_valid(int format)
+{
+ return (format & AF_FORMAT_MASK) == format;
+}
+
+int af_str2fmt_short(bstr str)
+{
+ if (bstr_startswith0(str, "0x")) {
+ bstr rest;
+ int fmt = bstrtoll(str, &rest, 16);
+ if (rest.len == 0 && af_fmt_valid(fmt))
+ return fmt;
+ }
+
+ for (int i = 0; af_fmtstr_table[i].name; i++)
+ if (!bstrcasecmp0(str, af_fmtstr_table[i].name))
+ return af_fmtstr_table[i].format;
+
+ return -1;
+}
diff --git a/audio/format.h b/audio/format.h
new file mode 100644
index 0000000000..e60c0789b9
--- /dev/null
+++ b/audio/format.h
@@ -0,0 +1,137 @@
+/*
+ * The sample format system used lin libaf is based on bitmasks.
+ * The format definition only refers to the storage format,
+ * not the resolution.
+ *
+ * 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.
+ */
+
+#ifndef MPLAYER_AF_FORMAT_H
+#define MPLAYER_AF_FORMAT_H
+
+#include <sys/types.h>
+#include "config.h"
+#include "bstr.h"
+
+// Endianness
+#define AF_FORMAT_BE (0<<0) // Big Endian
+#define AF_FORMAT_LE (1<<0) // Little Endian
+#define AF_FORMAT_END_MASK (1<<0)
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define AF_FORMAT_NE AF_FORMAT_BE
+#else
+#define AF_FORMAT_NE AF_FORMAT_LE
+#endif
+
+// Signed/unsigned
+#define AF_FORMAT_SI (0<<1) // Signed
+#define AF_FORMAT_US (1<<1) // Unsigned
+#define AF_FORMAT_SIGN_MASK (1<<1)
+
+// Fixed or floating point
+#define AF_FORMAT_I (0<<2) // Int
+#define AF_FORMAT_F (1<<2) // Foating point
+#define AF_FORMAT_POINT_MASK (1<<2)
+
+// Bits used
+#define AF_FORMAT_8BIT (0<<3)
+#define AF_FORMAT_16BIT (1<<3)
+#define AF_FORMAT_24BIT (2<<3)
+#define AF_FORMAT_32BIT (3<<3)
+#define AF_FORMAT_40BIT (4<<3)
+#define AF_FORMAT_48BIT (5<<3)
+#define AF_FORMAT_BITS_MASK (7<<3)
+
+// Special flags refering to non pcm data
+#define AF_FORMAT_MU_LAW (1<<6)
+#define AF_FORMAT_A_LAW (2<<6)
+#define AF_FORMAT_MPEG2 (3<<6) // MPEG(2) audio
+#define AF_FORMAT_AC3 (4<<6) // Dolby Digital AC3
+#define AF_FORMAT_IMA_ADPCM (5<<6)
+#define AF_FORMAT_IEC61937 (6<<6)
+#define AF_FORMAT_SPECIAL_MASK (7<<6)
+
+#define AF_FORMAT_MASK ((1<<9)-1)
+
+// PREDEFINED formats
+
+#define AF_FORMAT_U8 (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE)
+#define AF_FORMAT_S8 (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT|AF_FORMAT_NE)
+#define AF_FORMAT_U16_LE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_LE)
+#define AF_FORMAT_U16_BE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_BE)
+#define AF_FORMAT_S16_LE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_LE)
+#define AF_FORMAT_S16_BE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_BE)
+#define AF_FORMAT_U24_LE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_LE)
+#define AF_FORMAT_U24_BE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_BE)
+#define AF_FORMAT_S24_LE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_LE)
+#define AF_FORMAT_S24_BE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_BE)
+#define AF_FORMAT_U32_LE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_LE)
+#define AF_FORMAT_U32_BE (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_BE)
+#define AF_FORMAT_S32_LE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_LE)
+#define AF_FORMAT_S32_BE (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_BE)
+
+#define AF_FORMAT_FLOAT_LE (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_LE)
+#define AF_FORMAT_FLOAT_BE (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_BE)
+
+#define AF_FORMAT_AC3_LE (AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_LE)
+#define AF_FORMAT_AC3_BE (AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_BE)
+
+#define AF_FORMAT_IEC61937_LE (AF_FORMAT_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_LE)
+#define AF_FORMAT_IEC61937_BE (AF_FORMAT_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_BE)
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define AF_FORMAT_U16_NE AF_FORMAT_U16_BE
+#define AF_FORMAT_S16_NE AF_FORMAT_S16_BE
+#define AF_FORMAT_U24_NE AF_FORMAT_U24_BE
+#define AF_FORMAT_S24_NE AF_FORMAT_S24_BE
+#define AF_FORMAT_U32_NE AF_FORMAT_U32_BE
+#define AF_FORMAT_S32_NE AF_FORMAT_S32_BE
+#define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_BE
+#define AF_FORMAT_AC3_NE AF_FORMAT_AC3_BE
+#define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_BE
+#else
+#define AF_FORMAT_U16_NE AF_FORMAT_U16_LE
+#define AF_FORMAT_S16_NE AF_FORMAT_S16_LE
+#define AF_FORMAT_U24_NE AF_FORMAT_U24_LE
+#define AF_FORMAT_S24_NE AF_FORMAT_S24_LE
+#define AF_FORMAT_U32_NE AF_FORMAT_U32_LE
+#define AF_FORMAT_S32_NE AF_FORMAT_S32_LE
+#define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_LE
+#define AF_FORMAT_AC3_NE AF_FORMAT_AC3_LE
+#define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_LE
+#endif
+
+#define AF_FORMAT_UNKNOWN (-1)
+
+#define AF_FORMAT_IS_AC3(fmt) (((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_AC3)
+#define AF_FORMAT_IS_IEC61937(fmt) (((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_IEC61937)
+
+struct af_fmt_entry {
+ const char *name;
+ int format;
+};
+
+extern const struct af_fmt_entry af_fmtstr_table[];
+
+int af_str2fmt_short(bstr str);
+int af_fmt2bits(int format);
+int af_bits2fmt(int bits);
+char* af_fmt2str(int format, char* str, int size);
+const char* af_fmt2str_short(int format);
+
+#endif /* MPLAYER_AF_FORMAT_H */
diff --git a/audio/mixer.c b/audio/mixer.c
new file mode 100644
index 0000000000..2f9505a1ae
--- /dev/null
+++ b/audio/mixer.c
@@ -0,0 +1,292 @@
+/*
+ * 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 <string.h>
+
+#include <libavutil/common.h>
+
+#include "config.h"
+#include "libao2/audio_out.h"
+#include "libaf/af.h"
+#include "mp_msg.h"
+#include "mixer.h"
+
+
+static void checkvolume(struct mixer *mixer)
+{
+ if (!mixer->ao)
+ return;
+
+ if (mixer->softvol == SOFTVOL_AUTO) {
+ mixer->softvol = mixer->ao->per_application_mixer
+ ? SOFTVOL_NO : SOFTVOL_YES;
+ }
+
+ ao_control_vol_t vol;
+ if (mixer->softvol || CONTROL_OK != ao_control(mixer->ao,
+ AOCONTROL_GET_VOLUME, &vol)) {
+ mixer->softvol = SOFTVOL_YES;
+ if (!mixer->afilter)
+ return;
+ float db_vals[AF_NCH];
+ if (!af_control_any_rev(mixer->afilter,
+ AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET, db_vals))
+ db_vals[0] = db_vals[1] = 1.0;
+ else
+ af_from_dB(2, db_vals, db_vals, 20.0, -200.0, 60.0);
+ vol.left = (db_vals[0] / (mixer->softvol_max / 100.0)) * 100.0;
+ vol.right = (db_vals[1] / (mixer->softvol_max / 100.0)) * 100.0;
+ }
+ float l = mixer->vol_l;
+ float r = mixer->vol_r;
+ if (mixer->muted_using_volume)
+ l = r = 0;
+ /* Try to detect cases where the volume has been changed by some external
+ * action (such as something else changing a shared system-wide volume).
+ * We don't test for exact equality, as some AOs may round the value
+ * we last set to some nearby supported value. 3 has been the default
+ * volume step for increase/decrease keys, and is apparently big enough
+ * to step to the next possible value in most setups.
+ */
+ if (FFABS(vol.left - l) >= 3 || FFABS(vol.right - r) >= 3) {
+ mixer->vol_l = vol.left;
+ mixer->vol_r = vol.right;
+ if (mixer->muted_using_volume)
+ mixer->muted = false;
+ }
+ if (!mixer->softvol)
+ // Rely on the value not changing if the query is not supported
+ ao_control(mixer->ao, AOCONTROL_GET_MUTE, &mixer->muted);
+ mixer->muted_by_us &= mixer->muted;
+ mixer->muted_using_volume &= mixer->muted;
+}
+
+void mixer_getvolume(mixer_t *mixer, float *l, float *r)
+{
+ checkvolume(mixer);
+ *l = mixer->vol_l;
+ *r = mixer->vol_r;
+}
+
+static void setvolume_internal(mixer_t *mixer, float l, float r)
+{
+ struct ao_control_vol vol = {.left = l, .right = r};
+ if (!mixer->softvol) {
+ // relies on the driver data being permanent (so ptr stays valid)
+ mixer->restore_volume = mixer->ao->no_persistent_volume ?
+ mixer->ao->driver->info->short_name : NULL;
+ if (ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK)
+ mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
+ "[Mixer] Failed to change audio output volume.\n");
+ return;
+ }
+ mixer->restore_volume = "softvol";
+ if (!mixer->afilter)
+ return;
+ // af_volume uses values in dB
+ float db_vals[AF_NCH];
+ int i;
+ db_vals[0] = (l / 100.0) * (mixer->softvol_max / 100.0);
+ db_vals[1] = (r / 100.0) * (mixer->softvol_max / 100.0);
+ for (i = 2; i < AF_NCH; i++)
+ db_vals[i] = ((l + r) / 100.0) * (mixer->softvol_max / 100.0) / 2.0;
+ af_to_dB(AF_NCH, db_vals, db_vals, 20.0);
+ if (!af_control_any_rev(mixer->afilter,
+ AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET,
+ db_vals))
+ {
+ mp_tmsg(MSGT_GLOBAL, mixer->softvol ? MSGL_V : MSGL_WARN,
+ "[Mixer] No hardware mixing, inserting volume filter.\n");
+ if (!(af_add(mixer->afilter, "volume")
+ && af_control_any_rev(mixer->afilter,
+ AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET,
+ db_vals)))
+ mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
+ "[Mixer] No volume control available.\n");
+ }
+}
+
+void mixer_setvolume(mixer_t *mixer, float l, float r)
+{
+ checkvolume(mixer); // to check mute status and AO support for volume
+ mixer->vol_l = av_clip(l, 0, 100);
+ mixer->vol_r = av_clip(r, 0, 100);
+ if (!mixer->ao || mixer->muted)
+ return;
+ setvolume_internal(mixer, mixer->vol_l, mixer->vol_r);
+}
+
+void mixer_getbothvolume(mixer_t *mixer, float *b)
+{
+ float mixer_l, mixer_r;
+ mixer_getvolume(mixer, &mixer_l, &mixer_r);
+ *b = (mixer_l + mixer_r) / 2;
+}
+
+void mixer_setmute(struct mixer *mixer, bool mute)
+{
+ checkvolume(mixer);
+ if (mute != mixer->muted) {
+ if (!mixer->softvol && !mixer->muted_using_volume && ao_control(
+ mixer->ao, AOCONTROL_SET_MUTE, &mute) == CONTROL_OK) {
+ mixer->muted_using_volume = false;
+ } else {
+ setvolume_internal(mixer, mixer->vol_l*!mute, mixer->vol_r*!mute);
+ mixer->muted_using_volume = mute;
+ }
+ mixer->muted = mute;
+ mixer->muted_by_us = mute;
+ }
+}
+
+bool mixer_getmute(struct mixer *mixer)
+{
+ checkvolume(mixer);
+ return mixer->muted;
+}
+
+static void addvolume(struct mixer *mixer, float d)
+{
+ checkvolume(mixer);
+ mixer_setvolume(mixer, mixer->vol_l + d, mixer->vol_r + d);
+ if (d > 0)
+ mixer_setmute(mixer, false);
+}
+
+void mixer_incvolume(mixer_t *mixer)
+{
+ addvolume(mixer, mixer->volstep);
+}
+
+void mixer_decvolume(mixer_t *mixer)
+{
+ addvolume(mixer, -mixer->volstep);
+}
+
+void mixer_getbalance(mixer_t *mixer, float *val)
+{
+ if (mixer->afilter)
+ af_control_any_rev(mixer->afilter,
+ AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET,
+ &mixer->balance);
+ *val = mixer->balance;
+}
+
+/* NOTE: Currently the balance code is seriously buggy: it always changes
+ * the af_pan mapping between the first two input channels and first two
+ * output channels to particular values. These values make sense for an
+ * af_pan instance that was automatically inserted for balance control
+ * only and is otherwise an identity transform, but if the filter was
+ * there for another reason, then ignoring and overriding the original
+ * values is completely wrong. In particular, this will break
+ * automatically inserted downmix filters; the original coefficients that
+ * are significantly below 1 will be overwritten with much higher values.
+ */
+
+void mixer_setbalance(mixer_t *mixer, float val)
+{
+ float level[AF_NCH];
+ int i;
+ af_control_ext_t arg_ext = { .arg = level };
+ struct af_instance *af_pan_balance;
+
+ mixer->balance = val;
+
+ if (!mixer->afilter)
+ return;
+
+ if (af_control_any_rev(mixer->afilter,
+ AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val))
+ return;
+
+ if (val == 0 || mixer->ao->channels < 2)
+ return;
+
+ if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) {
+ mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
+ "[Mixer] No balance control available.\n");
+ return;
+ }
+
+ af_init(mixer->afilter);
+ /* make all other channels pass thru since by default pan blocks all */
+ memset(level, 0, sizeof(level));
+ for (i = 2; i < AF_NCH; i++) {
+ arg_ext.ch = i;
+ level[i] = 1.f;
+ af_pan_balance->control(af_pan_balance,
+ AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET,
+ &arg_ext);
+ level[i] = 0.f;
+ }
+
+ af_pan_balance->control(af_pan_balance,
+ AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val);
+}
+
+// Called after the audio filter chain is built or rebuilt.
+void mixer_reinit(struct mixer *mixer, struct ao *ao)
+{
+ mixer->ao = ao;
+ /* Use checkvolume() to see if softvol needs to be enabled because of
+ * lacking AO support, but first store values it could overwrite. */
+ float left = mixer->vol_l, right = mixer->vol_r;
+ bool muted = mixer->muted_by_us;
+ checkvolume(mixer);
+ /* Try to avoid restoring volume stored from one control method with
+ * another. Especially, restoring softvol volume (typically high) on
+ * system mixer could have very nasty effects. */
+ const char *restore_reason = mixer->softvol ? "softvol" :
+ mixer->ao->driver->info->short_name;
+ if (mixer->restore_volume && !strcmp(mixer->restore_volume,
+ restore_reason))
+ mixer_setvolume(mixer, left, right);
+ /* We turn mute off at AO uninit, so it has to be restored (unless
+ * we're reinitializing filter chain while keeping AO); but we only
+ * enable mute, not turn external mute off. */
+ if (muted)
+ mixer_setmute(mixer, true);
+ if (mixer->balance != 0)
+ mixer_setbalance(mixer, mixer->balance);
+}
+
+/* Called before uninitializing the audio output. The main purpose is to
+ * turn off mute, in case it's a global/persistent setting which might
+ * otherwise be left enabled even after this player instance exits.
+ */
+void mixer_uninit(struct mixer *mixer)
+{
+ if (!mixer->ao)
+ return;
+
+ checkvolume(mixer);
+ if (mixer->muted_by_us) {
+ /* Current audio output API combines playing the remaining buffered
+ * audio and uninitializing the AO into one operation, even though
+ * ideally unmute would happen between those two steps. We can't do
+ * volume changes after uninitialization, but we don't want the
+ * remaining audio to play at full volume either. Thus this
+ * workaround to drop remaining audio first. */
+ ao_reset(mixer->ao);
+ mixer_setmute(mixer, false);
+ /* We remember mute status and re-enable it if we play more audio
+ * in the same process. */
+ mixer->muted_by_us = true;
+ }
+ mixer->ao = NULL;
+}
diff --git a/audio/mixer.h b/audio/mixer.h
new file mode 100644
index 0000000000..ba90d0881c
--- /dev/null
+++ b/audio/mixer.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_MIXER_H
+#define MPLAYER_MIXER_H
+
+#include <stdbool.h>
+
+#include "libaf/af.h"
+#include "libao2/audio_out.h"
+
+enum {
+ SOFTVOL_NO = 0,
+ SOFTVOL_YES = 1,
+ SOFTVOL_AUTO = 2,
+};
+
+typedef struct mixer {
+ struct ao *ao;
+ struct af_stream *afilter;
+ int volstep;
+ int softvol;
+ float softvol_max;
+ bool muted;
+ bool muted_by_us;
+ bool muted_using_volume;
+ float vol_l, vol_r;
+ /* Contains ao driver name or "softvol" if volume is not persistent
+ * and needs to be restored after the driver is reinitialized. */
+ const char *restore_volume;
+ float balance;
+} mixer_t;
+
+void mixer_reinit(struct mixer *mixer, struct ao *ao);
+void mixer_uninit(struct mixer *mixer);
+void mixer_getvolume(mixer_t *mixer, float *l, float *r);
+void mixer_setvolume(mixer_t *mixer, float l, float r);
+void mixer_incvolume(mixer_t *mixer);
+void mixer_decvolume(mixer_t *mixer);
+void mixer_getbothvolume(mixer_t *mixer, float *b);
+void mixer_setmute(mixer_t *mixer, bool mute);
+bool mixer_getmute(mixer_t *mixer);
+void mixer_getbalance(mixer_t *mixer, float *bal);
+void mixer_setbalance(mixer_t *mixer, float bal);
+
+#endif /* MPLAYER_MIXER_H */
diff --git a/audio/out/ao.c b/audio/out/ao.c
new file mode 100644
index 0000000000..ab8e60b753
--- /dev/null
+++ b/audio/out/ao.c
@@ -0,0 +1,294 @@
+/*
+ * 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 <string.h>
+#include <assert.h>
+
+#include "talloc.h"
+
+#include "config.h"
+#include "audio_out.h"
+
+#include "mp_msg.h"
+
+// there are some globals:
+struct ao *global_ao;
+char *ao_subdevice = NULL;
+
+extern const struct ao_driver audio_out_oss;
+extern const struct ao_driver audio_out_coreaudio;
+extern const struct ao_driver audio_out_rsound;
+extern const struct ao_driver audio_out_pulse;
+extern const struct ao_driver audio_out_jack;
+extern const struct ao_driver audio_out_openal;
+extern const struct ao_driver audio_out_null;
+extern const struct ao_driver audio_out_alsa;
+extern const struct ao_driver audio_out_dsound;
+extern const struct ao_driver audio_out_pcm;
+extern const struct ao_driver audio_out_pss;
+extern const struct ao_driver audio_out_lavc;
+extern const struct ao_driver audio_out_portaudio;
+
+static const struct ao_driver * const audio_out_drivers[] = {
+// native:
+#ifdef CONFIG_COREAUDIO
+ &audio_out_coreaudio,
+#endif
+#ifdef CONFIG_PULSE
+ &audio_out_pulse,
+#endif
+#ifdef CONFIG_ALSA
+ &audio_out_alsa,
+#endif
+#ifdef CONFIG_OSS_AUDIO
+ &audio_out_oss,
+#endif
+#ifdef CONFIG_PORTAUDIO
+ &audio_out_portaudio,
+#endif
+#ifdef CONFIG_DSOUND
+ &audio_out_dsound,
+#endif
+ // wrappers:
+#ifdef CONFIG_JACK
+ &audio_out_jack,
+#endif
+#ifdef CONFIG_OPENAL
+ &audio_out_openal,
+#endif
+ &audio_out_null,
+ // should not be auto-selected:
+ &audio_out_pcm,
+#ifdef CONFIG_ENCODING
+ &audio_out_lavc,
+#endif
+#ifdef CONFIG_RSOUND
+ &audio_out_rsound,
+#endif
+ NULL
+};
+
+void list_audio_out(void)
+{
+ int i=0;
+ mp_tmsg(MSGT_AO, MSGL_INFO, "Available audio output drivers:\n");
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_OUTPUTS\n");
+ while (audio_out_drivers[i]) {
+ const ao_info_t *info = audio_out_drivers[i++]->info;
+ mp_msg(MSGT_GLOBAL, MSGL_INFO, "\t%s\t%s\n", info->short_name,
+ info->name);
+ }
+ mp_msg(MSGT_GLOBAL, MSGL_INFO,"\n");
+}
+
+struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input)
+{
+ struct ao *r = talloc(NULL, struct ao);
+ *r = (struct ao){.outburst = 512, .buffersize = -1,
+ .opts = opts, .input_ctx = input };
+ return r;
+}
+
+void ao_init(struct ao *ao, char **ao_list)
+{
+ /* Caller adding child blocks is not supported as we may call
+ * talloc_free_children() to clean up after failed open attempts.
+ */
+ assert(talloc_total_blocks(ao) == 1);
+ struct ao backup = *ao;
+
+ if (!ao_list)
+ goto try_defaults;
+
+ // first try the preferred drivers, with their optional subdevice param:
+ while (*ao_list) {
+ char *ao_name = *ao_list;
+ if (!*ao_name)
+ goto try_defaults; // empty entry means try defaults
+ int ao_len;
+ char *params = strchr(ao_name, ':');
+ if (params) {
+ ao_len = params - ao_name;
+ params++;
+ } else
+ ao_len = strlen(ao_name);
+
+ mp_tmsg(MSGT_AO, MSGL_V,
+ "Trying preferred audio driver '%.*s', options '%s'\n",
+ ao_len, ao_name, params ? params : "[none]");
+
+ const struct ao_driver *audio_out = NULL;
+ for (int i = 0; audio_out_drivers[i]; i++) {
+ audio_out = audio_out_drivers[i];
+ if (!strncmp(audio_out->info->short_name, ao_name, ao_len))
+ break;
+ audio_out = NULL;
+ }
+ if (audio_out) {
+ // name matches, try it
+ ao->driver = audio_out;
+ if (audio_out->init(ao, params) >= 0) {
+ ao->driver = audio_out;
+ ao->initialized = true;
+ return;
+ }
+ mp_tmsg(MSGT_AO, MSGL_WARN,
+ "Failed to initialize audio driver '%s'\n", ao_name);
+ talloc_free_children(ao);
+ *ao = backup;
+ } else
+ mp_tmsg(MSGT_AO, MSGL_WARN, "No such audio driver '%.*s'\n",
+ ao_len, ao_name);
+ ++ao_list;
+ }
+ return;
+
+ try_defaults:
+ mp_tmsg(MSGT_AO, MSGL_V, "Trying every known audio driver...\n");
+
+ // now try the rest...
+ for (int i = 0; audio_out_drivers[i]; i++) {
+ const struct ao_driver *audio_out = audio_out_drivers[i];
+ ao->driver = audio_out;
+ ao->probing = true;
+ if (audio_out->init(ao, NULL) >= 0) {
+ ao->probing = false;
+ ao->initialized = true;
+ ao->driver = audio_out;
+ return;
+ }
+ talloc_free_children(ao);
+ *ao = backup;
+ }
+ return;
+}
+
+void ao_uninit(struct ao *ao, bool cut_audio)
+{
+ assert(ao->buffer.len >= ao->buffer_playable_size);
+ ao->buffer.len = ao->buffer_playable_size;
+ if (ao->initialized)
+ ao->driver->uninit(ao, cut_audio);
+ if (!cut_audio && ao->buffer.len)
+ mp_msg(MSGT_AO, MSGL_WARN, "Audio output truncated at end.\n");
+ talloc_free(ao);
+}
+
+int ao_play(struct ao *ao, void *data, int len, int flags)
+{
+ return ao->driver->play(ao, data, len, flags);
+}
+
+int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ if (ao->driver->control)
+ return ao->driver->control(ao, cmd, arg);
+ return CONTROL_UNKNOWN;
+}
+
+double ao_get_delay(struct ao *ao)
+{
+ if (!ao->driver->get_delay) {
+ assert(ao->untimed);
+ return 0;
+ }
+ return ao->driver->get_delay(ao);
+}
+
+int ao_get_space(struct ao *ao)
+{
+ return ao->driver->get_space(ao);
+}
+
+void ao_reset(struct ao *ao)
+{
+ ao->buffer.len = 0;
+ ao->buffer_playable_size = 0;
+ if (ao->driver->reset)
+ ao->driver->reset(ao);
+}
+
+void ao_pause(struct ao *ao)
+{
+ if (ao->driver->pause)
+ ao->driver->pause(ao);
+}
+
+void ao_resume(struct ao *ao)
+{
+ if (ao->driver->resume)
+ ao->driver->resume(ao);
+}
+
+
+
+int old_ao_init(struct ao *ao, char *params)
+{
+ assert(!global_ao);
+ global_ao = ao;
+ ao_subdevice = params ? talloc_strdup(ao, params) : NULL;
+ if (ao->driver->old_functions->init(ao->samplerate, ao->channels,
+ ao->format, 0) == 0) {
+ global_ao = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+void old_ao_uninit(struct ao *ao, bool cut_audio)
+{
+ ao->driver->old_functions->uninit(cut_audio);
+ global_ao = NULL;
+}
+
+int old_ao_play(struct ao *ao, void *data, int len, int flags)
+{
+ return ao->driver->old_functions->play(data, len, flags);
+}
+
+int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ return ao->driver->old_functions->control(cmd, arg);
+}
+
+float old_ao_get_delay(struct ao *ao)
+{
+ return ao->driver->old_functions->get_delay();
+}
+
+int old_ao_get_space(struct ao *ao)
+{
+ return ao->driver->old_functions->get_space();
+}
+
+void old_ao_reset(struct ao *ao)
+{
+ ao->driver->old_functions->reset();
+}
+
+void old_ao_pause(struct ao *ao)
+{
+ ao->driver->old_functions->pause();
+}
+
+void old_ao_resume(struct ao *ao)
+{
+ ao->driver->old_functions->resume();
+}
diff --git a/audio/out/ao.h b/audio/out/ao.h
new file mode 100644
index 0000000000..9e172fd06c
--- /dev/null
+++ b/audio/out/ao.h
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_AUDIO_OUT_H
+#define MPLAYER_AUDIO_OUT_H
+
+#include <stdbool.h>
+
+#include "bstr.h"
+
+#define CONTROL_OK 1
+#define CONTROL_TRUE 1
+#define CONTROL_FALSE 0
+#define CONTROL_UNKNOWN -1
+#define CONTROL_ERROR -2
+#define CONTROL_NA -3
+
+enum aocontrol {
+ // _VOLUME commands take struct ao_control_vol pointer for input/output.
+ // If there's only one volume, SET should use average of left/right.
+ AOCONTROL_GET_VOLUME,
+ AOCONTROL_SET_VOLUME,
+ // _MUTE commands take a pointer to bool
+ AOCONTROL_GET_MUTE,
+ AOCONTROL_SET_MUTE,
+};
+
+#define AOPLAY_FINAL_CHUNK 1
+
+typedef struct ao_control_vol {
+ float left;
+ float right;
+} ao_control_vol_t;
+
+typedef struct ao_info {
+ /* driver name ("Matrox Millennium G200/G400" */
+ const char *name;
+ /* short name (for config strings) ("mga") */
+ const char *short_name;
+ /* author ("Aaron Holtzman <aholtzma@ess.engr.uvic.ca>") */
+ const char *author;
+ /* any additional comments */
+ const char *comment;
+} ao_info_t;
+
+/* interface towards mplayer and */
+typedef struct ao_old_functions {
+ int (*control)(int cmd, void *arg);
+ int (*init)(int rate, int channels, int format, int flags);
+ void (*uninit)(int immed);
+ void (*reset)(void);
+ int (*get_space)(void);
+ int (*play)(void *data, int len, int flags);
+ float (*get_delay)(void);
+ void (*pause)(void);
+ void (*resume)(void);
+} ao_functions_t;
+
+struct ao;
+
+struct ao_driver {
+ bool is_new;
+ const struct ao_info *info;
+ const struct ao_old_functions *old_functions;
+ int (*control)(struct ao *ao, enum aocontrol cmd, void *arg);
+ int (*init)(struct ao *ao, char *params);
+ void (*uninit)(struct ao *ao, bool cut_audio);
+ void (*reset)(struct ao*ao);
+ int (*get_space)(struct ao *ao);
+ int (*play)(struct ao *ao, void *data, int len, int flags);
+ float (*get_delay)(struct ao *ao);
+ void (*pause)(struct ao *ao);
+ void (*resume)(struct ao *ao);
+};
+
+/* global data used by mplayer and plugins */
+struct ao {
+ int samplerate;
+ int channels;
+ int format;
+ int bps;
+ int outburst;
+ int buffersize;
+ int brokenpts;
+ double pts;
+ struct bstr buffer;
+ int buffer_playable_size;
+ bool probing;
+ bool initialized;
+ bool untimed;
+ bool no_persistent_volume;
+ bool per_application_mixer;
+ const struct ao_driver *driver;
+ void *priv;
+ struct encode_lavc_context *encode_lavc_ctx;
+ struct MPOpts *opts;
+ struct input_ctx *input_ctx;
+};
+
+extern char *ao_subdevice;
+
+void list_audio_out(void);
+
+struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input);
+void ao_init(struct ao *ao, char **ao_list);
+void ao_uninit(struct ao *ao, bool cut_audio);
+int ao_play(struct ao *ao, void *data, int len, int flags);
+int ao_control(struct ao *ao, enum aocontrol cmd, void *arg);
+double ao_get_delay(struct ao *ao);
+int ao_get_space(struct ao *ao);
+void ao_reset(struct ao *ao);
+void ao_pause(struct ao *ao);
+void ao_resume(struct ao *ao);
+
+int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg);
+int old_ao_init(struct ao *ao, char *params);
+void old_ao_uninit(struct ao *ao, bool cut_audio);
+void old_ao_reset(struct ao*ao);
+int old_ao_get_space(struct ao *ao);
+int old_ao_play(struct ao *ao, void *data, int len, int flags);
+float old_ao_get_delay(struct ao *ao);
+void old_ao_pause(struct ao *ao);
+void old_ao_resume(struct ao *ao);
+
+#endif /* MPLAYER_AUDIO_OUT_H */
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
new file mode 100644
index 0000000000..27119112cb
--- /dev/null
+++ b/audio/out/ao_alsa.c
@@ -0,0 +1,868 @@
+/*
+ * ALSA 0.9.x-1.x audio output driver
+ *
+ * Copyright (C) 2004 Alex Beregszaszi
+ *
+ * modified for real ALSA 0.9.0 support by Zsolt Barat <joy@streamminister.de>
+ * additional AC-3 passthrough support by Andy Lo A Foe <andy@alsaplayer.org>
+ * 08/22/2002 iec958-init rewritten and merged with common init, zsolt
+ * 04/13/2004 merged with ao_alsa1.x, fixes provided by Jindrich Makovicka
+ * 04/25/2004 printfs converted to mp_msg, Zsolt.
+ *
+ * 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 <errno.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include <alloca.h>
+
+#include "config.h"
+#include "subopt-helper.h"
+#include "mixer.h"
+#include "mp_msg.h"
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <alsa/asoundlib.h>
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "libaf/format.h"
+
+static const ao_info_t info =
+{
+ "ALSA-0.9.x-1.x audio output",
+ "alsa",
+ "Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>",
+ "under development"
+};
+
+LIBAO_EXTERN(alsa)
+
+static snd_pcm_t *alsa_handler;
+static snd_pcm_format_t alsa_format;
+
+#define BUFFER_TIME 500000 // 0.5 s
+#define FRAGCOUNT 16
+
+static size_t bytes_per_sample;
+
+static int alsa_can_pause;
+static snd_pcm_sframes_t prepause_frames;
+
+#define ALSA_DEVICE_SIZE 256
+
+static void alsa_error_handler(const char *file, int line, const char *function,
+ int err, const char *format, ...)
+{
+ char tmp[0xc00];
+ va_list va;
+
+ va_start(va, format);
+ vsnprintf(tmp, sizeof tmp, format, va);
+ va_end(va);
+
+ if (err)
+ mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s: %s\n",
+ file, line, function, tmp, snd_strerror(err));
+ else
+ mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s\n",
+ file, line, function, tmp);
+}
+
+/* to set/get/query special features/parameters */
+static int control(int cmd, void *arg)
+{
+ switch(cmd) {
+ case AOCONTROL_GET_MUTE:
+ case AOCONTROL_SET_MUTE:
+ case AOCONTROL_GET_VOLUME:
+ case AOCONTROL_SET_VOLUME:
+ {
+ int err;
+ snd_mixer_t *handle;
+ snd_mixer_elem_t *elem;
+ snd_mixer_selem_id_t *sid;
+
+ char *mix_name = "Master";
+ char *card = "default";
+ int mix_index = 0;
+
+ long pmin, pmax;
+ long get_vol, set_vol;
+ float f_multi;
+
+ if(AF_FORMAT_IS_AC3(ao_data.format) || AF_FORMAT_IS_IEC61937(ao_data.format))
+ return CONTROL_TRUE;
+
+ if(mixer_channel) {
+ char *test_mix_index;
+
+ mix_name = strdup(mixer_channel);
+ if ((test_mix_index = strchr(mix_name, ','))){
+ *test_mix_index = 0;
+ test_mix_index++;
+ mix_index = strtol(test_mix_index, &test_mix_index, 0);
+
+ if (*test_mix_index){
+ mp_tmsg(MSGT_AO,MSGL_ERR,
+ "[AO_ALSA] Invalid mixer index. Defaulting to 0.\n");
+ mix_index = 0 ;
+ }
+ }
+ }
+ if(mixer_device) card = mixer_device;
+
+ //allocate simple id
+ snd_mixer_selem_id_alloca(&sid);
+
+ //sets simple-mixer index and name
+ snd_mixer_selem_id_set_index(sid, mix_index);
+ snd_mixer_selem_id_set_name(sid, mix_name);
+
+ if (mixer_channel) {
+ free(mix_name);
+ mix_name = NULL;
+ }
+
+ if ((err = snd_mixer_open(&handle, 0)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer open error: %s\n", snd_strerror(err));
+ return CONTROL_ERROR;
+ }
+
+ if ((err = snd_mixer_attach(handle, card)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer attach %s error: %s\n",
+ card, snd_strerror(err));
+ snd_mixer_close(handle);
+ return CONTROL_ERROR;
+ }
+
+ if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer register error: %s\n", snd_strerror(err));
+ snd_mixer_close(handle);
+ return CONTROL_ERROR;
+ }
+ err = snd_mixer_load(handle);
+ if (err < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer load error: %s\n", snd_strerror(err));
+ snd_mixer_close(handle);
+ return CONTROL_ERROR;
+ }
+
+ elem = snd_mixer_find_selem(handle, sid);
+ if (!elem) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to find simple control '%s',%i.\n",
+ snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
+ snd_mixer_close(handle);
+ return CONTROL_ERROR;
+ }
+
+ snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
+ f_multi = (100 / (float)(pmax - pmin));
+
+ switch (cmd) {
+ case AOCONTROL_SET_VOLUME: {
+ ao_control_vol_t *vol = arg;
+ set_vol = vol->left / f_multi + pmin + 0.5;
+
+ //setting channels
+ if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting left channel, %s\n",
+ snd_strerror(err));
+ goto mixer_error;
+ }
+ mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol);
+
+ set_vol = vol->right / f_multi + pmin + 0.5;
+
+ if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting right channel, %s\n",
+ snd_strerror(err));
+ goto mixer_error;
+ }
+ mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n",
+ set_vol, pmin, pmax, f_multi);
+ break;
+ }
+ case AOCONTROL_GET_VOLUME: {
+ ao_control_vol_t *vol = arg;
+ snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol);
+ vol->left = (get_vol - pmin) * f_multi;
+ snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol);
+ vol->right = (get_vol - pmin) * f_multi;
+ mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right);
+ break;
+ }
+ case AOCONTROL_SET_MUTE: {
+ bool *mute = arg;
+ if (!snd_mixer_selem_has_playback_switch(elem))
+ goto mixer_error;
+ if (!snd_mixer_selem_has_playback_switch_joined(elem)) {
+ snd_mixer_selem_set_playback_switch(
+ elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute);
+ }
+ snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT,
+ !*mute);
+ break;
+ }
+ case AOCONTROL_GET_MUTE: {
+ bool *mute = arg;
+ if (!snd_mixer_selem_has_playback_switch(elem))
+ goto mixer_error;
+ int tmp = 1;
+ snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT,
+ &tmp);
+ *mute = !tmp;
+ if (!snd_mixer_selem_has_playback_switch_joined(elem)) {
+ snd_mixer_selem_get_playback_switch(
+ elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp);
+ *mute &= !tmp;
+ }
+ break;
+ }
+ }
+ snd_mixer_close(handle);
+ return CONTROL_OK;
+ mixer_error:
+ snd_mixer_close(handle);
+ return CONTROL_ERROR;
+ }
+
+ } //end switch
+ return CONTROL_UNKNOWN;
+}
+
+static void parse_device (char *dest, const char *src, int len)
+{
+ char *tmp;
+ memmove(dest, src, len);
+ dest[len] = 0;
+ while ((tmp = strrchr(dest, '.')))
+ tmp[0] = ',';
+ while ((tmp = strrchr(dest, '=')))
+ tmp[0] = ':';
+}
+
+static void print_help (void)
+{
+ mp_tmsg (MSGT_AO, MSGL_FATAL,
+ "\n[AO_ALSA] -ao alsa commandline help:\n"\
+ "[AO_ALSA] Example: mpv -ao alsa:device=hw=0.3\n"\
+ "[AO_ALSA] Sets first card fourth hardware device.\n\n"\
+ "[AO_ALSA] Options:\n"\
+ "[AO_ALSA] noblock\n"\
+ "[AO_ALSA] Opens device in non-blocking mode.\n"\
+ "[AO_ALSA] device=<device-name>\n"\
+ "[AO_ALSA] Sets device (change , to . and : to =)\n");
+}
+
+static int str_maxlen(void *strp) {
+ strarg_t *str = strp;
+ return str->len <= ALSA_DEVICE_SIZE;
+}
+
+static int try_open_device(const char *device, int open_mode, int try_ac3)
+{
+ int err, len;
+ char *ac3_device, *args;
+
+ if (try_ac3) {
+ /* to set the non-audio bit, use AES0=6 */
+ len = strlen(device);
+ ac3_device = malloc(len + 7 + 1);
+ if (!ac3_device)
+ return -ENOMEM;
+ strcpy(ac3_device, device);
+ args = strchr(ac3_device, ':');
+ if (!args) {
+ /* no existing parameters: add it behind device name */
+ strcat(ac3_device, ":AES0=6");
+ } else {
+ do
+ ++args;
+ while (isspace(*args));
+ if (*args == '\0') {
+ /* ":" but no parameters */
+ strcat(ac3_device, "AES0=6");
+ } else if (*args != '{') {
+ /* a simple list of parameters: add it at the end of the list */
+ strcat(ac3_device, ",AES0=6");
+ } else {
+ /* parameters in config syntax: add it inside the { } block */
+ do
+ --len;
+ while (len > 0 && isspace(ac3_device[len]));
+ if (ac3_device[len] == '}')
+ strcpy(ac3_device + len, " AES0=6}");
+ }
+ }
+ err = snd_pcm_open(&alsa_handler, ac3_device, SND_PCM_STREAM_PLAYBACK,
+ open_mode);
+ free(ac3_device);
+ if (!err)
+ return 0;
+ }
+ return snd_pcm_open(&alsa_handler, device, SND_PCM_STREAM_PLAYBACK,
+ open_mode);
+}
+
+/*
+ open & setup audio device
+ return: 1=success 0=fail
+*/
+static int init(int rate_hz, int channels, int format, int flags)
+{
+ int err;
+ int block;
+ strarg_t device;
+ snd_pcm_uframes_t chunk_size;
+ snd_pcm_uframes_t bufsize;
+ snd_pcm_uframes_t boundary;
+ const opt_t subopts[] = {
+ {"block", OPT_ARG_BOOL, &block, NULL},
+ {"device", OPT_ARG_STR, &device, str_maxlen},
+ {NULL}
+ };
+
+ char alsa_device[ALSA_DEVICE_SIZE + 1];
+ // make sure alsa_device is null-terminated even when using strncpy etc.
+ memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1);
+
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: requested format: %d Hz, %d channels, %x\n", rate_hz,
+ channels, format);
+ alsa_handler = NULL;
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: using ALSA %s\n", snd_asoundlib_version());
+
+ prepause_frames = 0;
+
+ snd_lib_error_set_handler(alsa_error_handler);
+
+ ao_data.samplerate = rate_hz;
+ ao_data.format = format;
+ ao_data.channels = channels;
+
+ switch (format)
+ {
+ case AF_FORMAT_S8:
+ alsa_format = SND_PCM_FORMAT_S8;
+ break;
+ case AF_FORMAT_U8:
+ alsa_format = SND_PCM_FORMAT_U8;
+ break;
+ case AF_FORMAT_U16_LE:
+ alsa_format = SND_PCM_FORMAT_U16_LE;
+ break;
+ case AF_FORMAT_U16_BE:
+ alsa_format = SND_PCM_FORMAT_U16_BE;
+ break;
+ case AF_FORMAT_AC3_LE:
+ case AF_FORMAT_S16_LE:
+ case AF_FORMAT_IEC61937_LE:
+ alsa_format = SND_PCM_FORMAT_S16_LE;
+ break;
+ case AF_FORMAT_AC3_BE:
+ case AF_FORMAT_S16_BE:
+ case AF_FORMAT_IEC61937_BE:
+ alsa_format = SND_PCM_FORMAT_S16_BE;
+ break;
+ case AF_FORMAT_U32_LE:
+ alsa_format = SND_PCM_FORMAT_U32_LE;
+ break;
+ case AF_FORMAT_U32_BE:
+ alsa_format = SND_PCM_FORMAT_U32_BE;
+ break;
+ case AF_FORMAT_S32_LE:
+ alsa_format = SND_PCM_FORMAT_S32_LE;
+ break;
+ case AF_FORMAT_S32_BE:
+ alsa_format = SND_PCM_FORMAT_S32_BE;
+ break;
+ case AF_FORMAT_U24_LE:
+ alsa_format = SND_PCM_FORMAT_U24_3LE;
+ break;
+ case AF_FORMAT_U24_BE:
+ alsa_format = SND_PCM_FORMAT_U24_3BE;
+ break;
+ case AF_FORMAT_S24_LE:
+ alsa_format = SND_PCM_FORMAT_S24_3LE;
+ break;
+ case AF_FORMAT_S24_BE:
+ alsa_format = SND_PCM_FORMAT_S24_3BE;
+ break;
+ case AF_FORMAT_FLOAT_LE:
+ alsa_format = SND_PCM_FORMAT_FLOAT_LE;
+ break;
+ case AF_FORMAT_FLOAT_BE:
+ alsa_format = SND_PCM_FORMAT_FLOAT_BE;
+ break;
+ case AF_FORMAT_MU_LAW:
+ alsa_format = SND_PCM_FORMAT_MU_LAW;
+ break;
+ case AF_FORMAT_A_LAW:
+ alsa_format = SND_PCM_FORMAT_A_LAW;
+ break;
+
+ default:
+ alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1
+ break;
+ }
+
+ //subdevice parsing
+ // set defaults
+ block = 1;
+ /* switch for spdif
+ * sets opening sequence for SPDIF
+ * sets also the playback and other switches 'on the fly'
+ * while opening the abstract alias for the spdif subdevice
+ * 'iec958'
+ */
+ if (AF_FORMAT_IS_AC3(format) || AF_FORMAT_IS_IEC61937(format)) {
+ device.str = "iec958";
+ mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", channels);
+ }
+ else
+ /* in any case for multichannel playback we should select
+ * appropriate device
+ */
+ switch (channels) {
+ case 1:
+ case 2:
+ device.str = "default";
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n");
+ break;
+ case 4:
+ if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
+ // hack - use the converter plugin
+ device.str = "plug:surround40";
+ else
+ device.str = "surround40";
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n");
+ break;
+ case 6:
+ if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
+ device.str = "plug:surround51";
+ else
+ device.str = "surround51";
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n");
+ break;
+ case 8:
+ if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
+ device.str = "plug:surround71";
+ else
+ device.str = "surround71";
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround71\n");
+ break;
+ default:
+ device.str = "default";
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] %d channels are not supported.\n",channels);
+ }
+ device.len = strlen(device.str);
+ if (subopt_parse(ao_subdevice, subopts) != 0) {
+ print_help();
+ return 0;
+ }
+ parse_device(alsa_device, device.str, device.len);
+
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: using device %s\n", alsa_device);
+
+ alsa_can_pause = 1;
+
+ if (!alsa_handler) {
+ int open_mode = block ? 0 : SND_PCM_NONBLOCK;
+ int isac3 = AF_FORMAT_IS_AC3(format) || AF_FORMAT_IS_IEC61937(format);
+ //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
+ if ((err = try_open_device(alsa_device, open_mode, isac3)) < 0)
+ {
+ if (err != -EBUSY && !block) {
+ mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Open in nonblock-mode failed, trying to open in block-mode.\n");
+ if ((err = try_open_device(alsa_device, 0, isac3)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err));
+ return 0;
+ }
+ } else {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err));
+ return 0;
+ }
+ }
+
+ if ((err = snd_pcm_nonblock(alsa_handler, 0)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AL_ALSA] Error setting block-mode %s.\n", snd_strerror(err));
+ } else {
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: pcm opened in blocking mode\n");
+ }
+
+ snd_pcm_hw_params_t *alsa_hwparams;
+ snd_pcm_sw_params_t *alsa_swparams;
+
+ snd_pcm_hw_params_alloca(&alsa_hwparams);
+ snd_pcm_sw_params_alloca(&alsa_swparams);
+
+ // setting hw-parameters
+ if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get initial parameters: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (err < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set access type: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ /* workaround for nonsupported formats
+ sets default format to S16_LE if the given formats aren't supported */
+ if ((err = snd_pcm_hw_params_test_format(alsa_handler, alsa_hwparams,
+ alsa_format)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_INFO,
+ "[AO_ALSA] Format %s is not supported by hardware, trying default.\n", af_fmt2str_short(format));
+ alsa_format = SND_PCM_FORMAT_S16_LE;
+ if (AF_FORMAT_IS_AC3(ao_data.format))
+ ao_data.format = AF_FORMAT_AC3_LE;
+ else if (AF_FORMAT_IS_IEC61937(ao_data.format))
+ ao_data.format = AF_FORMAT_IEC61937_LE;
+ else
+ ao_data.format = AF_FORMAT_S16_LE;
+ }
+
+ if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
+ alsa_format)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set format: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ if ((err = snd_pcm_hw_params_set_channels_near(alsa_handler, alsa_hwparams,
+ &ao_data.channels)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set channels: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11)
+ prefer our own resampler, since that allows users to choose the resampler,
+ even per file if desired */
+ if ((err = snd_pcm_hw_params_set_rate_resample(alsa_handler, alsa_hwparams,
+ 0)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to disable resampling: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams,
+ &ao_data.samplerate, NULL)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set samplerate-2: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ bytes_per_sample = af_fmt2bits(ao_data.format) / 8;
+ bytes_per_sample *= ao_data.channels;
+ ao_data.bps = ao_data.samplerate * bytes_per_sample;
+
+ if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams,
+ &(unsigned int){BUFFER_TIME}, NULL)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set buffer time near: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ if ((err = snd_pcm_hw_params_set_periods_near(alsa_handler, alsa_hwparams,
+ &(unsigned int){FRAGCOUNT}, NULL)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set periods: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+
+ /* finally install hardware parameters */
+ if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set hw-parameters: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+ // end setting hw-params
+
+
+ // gets buffersize for control
+ if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get buffersize: %s\n", snd_strerror(err));
+ return 0;
+ }
+ else {
+ ao_data.buffersize = bufsize * bytes_per_sample;
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize);
+ }
+
+ if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO ALSA] Unable to get period size: %s\n", snd_strerror(err));
+ return 0;
+ } else {
+ mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size);
+ }
+ ao_data.outburst = chunk_size * bytes_per_sample;
+
+ /* setting software parameters */
+ if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get sw-parameters: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+ if ((err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get boundary: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+ /* start playing when one period has been written */
+ if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, chunk_size)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set start threshold: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+ /* disable underrun reporting */
+ if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, boundary)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set stop threshold: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+ /* play silence when there is an underrun */
+ if ((err = snd_pcm_sw_params_set_silence_size(alsa_handler, alsa_swparams, boundary)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set silence size: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+ if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get sw-parameters: %s\n",
+ snd_strerror(err));
+ return 0;
+ }
+ /* end setting sw-params */
+
+ alsa_can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
+
+ mp_msg(MSGT_AO,MSGL_V,"alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n",
+ ao_data.samplerate, ao_data.channels, (int)bytes_per_sample, ao_data.buffersize,
+ snd_pcm_format_description(alsa_format));
+
+ } // end switch alsa_handler (spdif)
+ return 1;
+} // end init
+
+
+/* close audio device */
+static void uninit(int immed)
+{
+
+ if (alsa_handler) {
+ int err;
+
+ if (!immed)
+ snd_pcm_drain(alsa_handler);
+
+ if ((err = snd_pcm_close(alsa_handler)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm close error: %s\n", snd_strerror(err));
+ return;
+ }
+ else {
+ alsa_handler = NULL;
+ mp_msg(MSGT_AO,MSGL_V,"alsa-uninit: pcm closed\n");
+ }
+ }
+ else {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] No handler defined!\n");
+ }
+}
+
+static void audio_pause(void)
+{
+ int err;
+
+ if (alsa_can_pause) {
+ if ((err = snd_pcm_pause(alsa_handler, 1)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm pause error: %s\n", snd_strerror(err));
+ return;
+ }
+ mp_msg(MSGT_AO,MSGL_V,"alsa-pause: pause supported by hardware\n");
+ } else {
+ if (snd_pcm_delay(alsa_handler, &prepause_frames) < 0
+ || prepause_frames < 0)
+ prepause_frames = 0;
+
+ if ((err = snd_pcm_drop(alsa_handler)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm drop error: %s\n", snd_strerror(err));
+ return;
+ }
+ }
+}
+
+static void audio_resume(void)
+{
+ int err;
+
+ if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
+ mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Pcm in suspend mode, trying to resume.\n");
+ while ((err = snd_pcm_resume(alsa_handler)) == -EAGAIN) sleep(1);
+ }
+ if (alsa_can_pause) {
+ if ((err = snd_pcm_pause(alsa_handler, 0)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm resume error: %s\n", snd_strerror(err));
+ return;
+ }
+ mp_msg(MSGT_AO,MSGL_V,"alsa-resume: resume supported by hardware\n");
+ } else {
+ if ((err = snd_pcm_prepare(alsa_handler)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err));
+ return;
+ }
+ if (prepause_frames) {
+ void *silence = calloc(prepause_frames, bytes_per_sample);
+ play(silence, prepause_frames * bytes_per_sample, 0);
+ free(silence);
+ }
+ }
+}
+
+/* stop playing and empty buffers (for seeking/pause) */
+static void reset(void)
+{
+ int err;
+
+ prepause_frames = 0;
+ if ((err = snd_pcm_drop(alsa_handler)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err));
+ return;
+ }
+ if ((err = snd_pcm_prepare(alsa_handler)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err));
+ return;
+ }
+ return;
+}
+
+/*
+ plays 'len' bytes of 'data'
+ returns: number of bytes played
+ modified last at 29.06.02 by jp
+ thanxs for marius <marius@rospot.com> for giving us the light ;)
+*/
+
+static int play(void* data, int len, int flags)
+{
+ int num_frames;
+ snd_pcm_sframes_t res = 0;
+ if (!(flags & AOPLAY_FINAL_CHUNK))
+ len = len / ao_data.outburst * ao_data.outburst;
+ num_frames = len / bytes_per_sample;
+
+ //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len);
+
+ if (!alsa_handler) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Device configuration error.");
+ return 0;
+ }
+
+ if (num_frames == 0)
+ return 0;
+
+ do {
+ res = snd_pcm_writei(alsa_handler, data, num_frames);
+
+ if (res == -EINTR) {
+ /* nothing to do */
+ res = 0;
+ }
+ else if (res == -ESTRPIPE) { /* suspend */
+ mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Pcm in suspend mode, trying to resume.\n");
+ while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
+ sleep(1);
+ }
+ if (res < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Write error: %s\n", snd_strerror(res));
+ mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Trying to reset soundcard.\n");
+ if ((res = snd_pcm_prepare(alsa_handler)) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(res));
+ return 0;
+ break;
+ }
+ }
+ } while (res == 0);
+
+ return res < 0 ? res : res * bytes_per_sample;
+}
+
+/* how many byes are free in the buffer */
+static int get_space(void)
+{
+ snd_pcm_status_t *status;
+ int ret;
+
+ snd_pcm_status_alloca(&status);
+
+ if ((ret = snd_pcm_status(alsa_handler, status)) < 0)
+ {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Cannot get pcm status: %s\n", snd_strerror(ret));
+ return 0;
+ }
+
+ unsigned space = snd_pcm_status_get_avail(status) * bytes_per_sample;
+ if (space > ao_data.buffersize) // Buffer underrun?
+ space = ao_data.buffersize;
+ return space;
+}
+
+/* delay in seconds between first and last sample in buffer */
+static float get_delay(void)
+{
+ if (alsa_handler) {
+ snd_pcm_sframes_t delay;
+
+ if (snd_pcm_delay(alsa_handler, &delay) < 0)
+ return 0;
+
+ if (delay < 0) {
+ /* underrun - move the application pointer forward to catch up */
+ snd_pcm_forward(alsa_handler, -delay);
+ delay = 0;
+ }
+ return (float)delay / (float)ao_data.samplerate;
+ } else {
+ return 0;
+ }
+}
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
new file mode 100644
index 0000000000..146cfd2a22
--- /dev/null
+++ b/audio/out/ao_coreaudio.c
@@ -0,0 +1,1283 @@
+/*
+ * CoreAudio audio output driver for Mac OS X
+ *
+ * original copyright (C) Timothy J. Wood - Aug 2000
+ * ported to MPlayer libao2 by Dan Christiansen
+ *
+ * The S/PDIF part of the code is based on the auhal audio output
+ * module from VideoLAN:
+ * Copyright (c) 2006 Derk-Jan Hartman <hartman at videolan dot org>
+ *
+ * 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
+ * along with MPlayer; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * The MacOS X CoreAudio framework doesn't mesh as simply as some
+ * simpler frameworks do. This is due to the fact that CoreAudio pulls
+ * audio samples rather than having them pushed at it (which is nice
+ * when you are wanting to do good buffering of audio).
+ *
+ * AC-3 and MPEG audio passthrough is possible, but has never been tested
+ * due to lack of a soundcard that supports it.
+ */
+
+#include <CoreServices/CoreServices.h>
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "libaf/format.h"
+#include "osdep/timer.h"
+#include "libavutil/fifo.h"
+#include "subopt-helper.h"
+
+static const ao_info_t info =
+ {
+ "Darwin/Mac OS X native audio output",
+ "coreaudio",
+ "Timothy J. Wood & Dan Christiansen & Chris Roccati",
+ ""
+ };
+
+LIBAO_EXTERN(coreaudio)
+
+/* Prefix for all mp_msg() calls */
+#define ao_msg(a, b, c...) mp_msg(a, b, "AO: [coreaudio] " c)
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1040
+/* AudioDeviceIOProcID does not exist in Mac OS X 10.4. We can emulate
+ * this by using AudioDeviceAddIOProc() and AudioDeviceRemoveIOProc(). */
+#define AudioDeviceIOProcID AudioDeviceIOProc
+#define AudioDeviceDestroyIOProcID AudioDeviceRemoveIOProc
+static OSStatus AudioDeviceCreateIOProcID(AudioDeviceID dev,
+ AudioDeviceIOProc proc,
+ void *data,
+ AudioDeviceIOProcID *procid)
+{
+ *procid = proc;
+ return AudioDeviceAddIOProc(dev, proc, data);
+}
+#endif
+
+typedef struct ao_coreaudio_s
+{
+ AudioDeviceID i_selected_dev; /* Keeps DeviceID of the selected device. */
+ int b_supports_digital; /* Does the currently selected device support digital mode? */
+ int b_digital; /* Are we running in digital mode? */
+ int b_muted; /* Are we muted in digital mode? */
+
+ AudioDeviceIOProcID renderCallback; /* Render callback used for SPDIF */
+
+ /* AudioUnit */
+ AudioUnit theOutputUnit;
+
+ /* CoreAudio SPDIF mode specific */
+ pid_t i_hog_pid; /* Keeps the pid of our hog status. */
+ AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */
+ int i_stream_index; /* The index of i_stream_id in an AudioBufferList */
+ AudioStreamBasicDescription stream_format;/* The format we changed the stream to */
+ AudioStreamBasicDescription sfmt_revert; /* The original format of the stream */
+ int b_revert; /* Whether we need to revert the stream format */
+ int b_changed_mixing; /* Whether we need to set the mixing mode back */
+ int b_stream_format_changed; /* Flag for main thread to reset stream's format to digital and reset buffer */
+
+ /* Original common part */
+ int packetSize;
+ int paused;
+
+ /* Ring-buffer */
+ AVFifoBuffer *buffer;
+ unsigned int buffer_len; ///< must always be num_chunks * chunk_size
+ unsigned int num_chunks;
+ unsigned int chunk_size;
+} ao_coreaudio_t;
+
+static ao_coreaudio_t *ao = NULL;
+
+/**
+ * \brief add data to ringbuffer
+ */
+static int write_buffer(unsigned char* data, int len){
+ int free = ao->buffer_len - av_fifo_size(ao->buffer);
+ if (len > free) len = free;
+ return av_fifo_generic_write(ao->buffer, data, len, NULL);
+}
+
+/**
+ * \brief remove data from ringbuffer
+ */
+static int read_buffer(unsigned char* data,int len){
+ int buffered = av_fifo_size(ao->buffer);
+ if (len > buffered) len = buffered;
+ if (data)
+ av_fifo_generic_read(ao->buffer, data, len, NULL);
+ else
+ av_fifo_drain(ao->buffer, len);
+ return len;
+}
+
+static OSStatus theRenderProc(void *inRefCon,
+ AudioUnitRenderActionFlags *inActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber, UInt32 inNumFrames,
+ AudioBufferList *ioData)
+{
+int amt=av_fifo_size(ao->buffer);
+int req=(inNumFrames)*ao->packetSize;
+
+ if(amt>req)
+ amt=req;
+
+ if(amt)
+ read_buffer((unsigned char *)ioData->mBuffers[0].mData, amt);
+ else audio_pause();
+ ioData->mBuffers[0].mDataByteSize = amt;
+
+ return noErr;
+}
+
+static int control(int cmd,void *arg){
+ao_control_vol_t *control_vol;
+OSStatus err;
+Float32 vol;
+ switch (cmd) {
+ case AOCONTROL_GET_VOLUME:
+ control_vol = (ao_control_vol_t*)arg;
+ if (ao->b_digital) {
+ // Digital output has no volume adjust.
+ int vol = ao->b_muted ? 0 : 100;
+ *control_vol = (ao_control_vol_t) {
+ .left = vol, .right = vol,
+ };
+ return CONTROL_TRUE;
+ }
+ err = AudioUnitGetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &vol);
+
+ if(err==0) {
+ // printf("GET VOL=%f\n", vol);
+ control_vol->left=control_vol->right=vol*100.0/4.0;
+ return CONTROL_TRUE;
+ }
+ else {
+ ao_msg(MSGT_AO, MSGL_WARN, "could not get HAL output volume: [%4.4s]\n", (char *)&err);
+ return CONTROL_FALSE;
+ }
+
+ case AOCONTROL_SET_VOLUME:
+ control_vol = (ao_control_vol_t*)arg;
+
+ if (ao->b_digital) {
+ // Digital output can not set volume. Here we have to return true
+ // to make mixer forget it. Else mixer will add a soft filter,
+ // that's not we expected and the filter not support ac3 stream
+ // will cause mplayer die.
+
+ // Although not support set volume, but at least we support mute.
+ // MPlayer set mute by set volume to zero, we handle it.
+ if (control_vol->left == 0 && control_vol->right == 0)
+ ao->b_muted = 1;
+ else
+ ao->b_muted = 0;
+ return CONTROL_TRUE;
+ }
+
+ vol=(control_vol->left+control_vol->right)*4.0/200.0;
+ err = AudioUnitSetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0);
+ if(err==0) {
+ // printf("SET VOL=%f\n", vol);
+ return CONTROL_TRUE;
+ }
+ else {
+ ao_msg(MSGT_AO, MSGL_WARN, "could not set HAL output volume: [%4.4s]\n", (char *)&err);
+ return CONTROL_FALSE;
+ }
+ /* Everything is currently unimplemented */
+ default:
+ return CONTROL_FALSE;
+ }
+
+}
+
+
+static void print_format(int lev, const char* str, const AudioStreamBasicDescription *f){
+ uint32_t flags=(uint32_t) f->mFormatFlags;
+ ao_msg(MSGT_AO,lev, "%s %7.1fHz %"PRIu32"bit [%c%c%c%c][%"PRIu32"][%"PRIu32"][%"PRIu32"][%"PRIu32"][%"PRIu32"] %s %s %s%s%s%s\n",
+ str, f->mSampleRate, f->mBitsPerChannel,
+ (int)(f->mFormatID & 0xff000000) >> 24,
+ (int)(f->mFormatID & 0x00ff0000) >> 16,
+ (int)(f->mFormatID & 0x0000ff00) >> 8,
+ (int)(f->mFormatID & 0x000000ff) >> 0,
+ f->mFormatFlags, f->mBytesPerPacket,
+ f->mFramesPerPacket, f->mBytesPerFrame,
+ f->mChannelsPerFrame,
+ (flags&kAudioFormatFlagIsFloat) ? "float" : "int",
+ (flags&kAudioFormatFlagIsBigEndian) ? "BE" : "LE",
+ (flags&kAudioFormatFlagIsSignedInteger) ? "S" : "U",
+ (flags&kAudioFormatFlagIsPacked) ? " packed" : "",
+ (flags&kAudioFormatFlagIsAlignedHigh) ? " aligned" : "",
+ (flags&kAudioFormatFlagIsNonInterleaved) ? " ni" : "" );
+}
+
+static OSStatus GetAudioProperty(AudioObjectID id,
+ AudioObjectPropertySelector selector,
+ UInt32 outSize, void *outData)
+{
+ AudioObjectPropertyAddress property_address;
+
+ property_address.mSelector = selector;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ return AudioObjectGetPropertyData(id, &property_address, 0, NULL, &outSize, outData);
+}
+
+static UInt32 GetAudioPropertyArray(AudioObjectID id,
+ AudioObjectPropertySelector selector,
+ AudioObjectPropertyScope scope,
+ void **outData)
+{
+ OSStatus err;
+ AudioObjectPropertyAddress property_address;
+ UInt32 i_param_size;
+
+ property_address.mSelector = selector;
+ property_address.mScope = scope;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ err = AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, &i_param_size);
+
+ if (err != noErr)
+ return 0;
+
+ *outData = malloc(i_param_size);
+
+
+ err = AudioObjectGetPropertyData(id, &property_address, 0, NULL, &i_param_size, *outData);
+
+ if (err != noErr) {
+ free(*outData);
+ return 0;
+ }
+
+ return i_param_size;
+}
+
+static UInt32 GetGlobalAudioPropertyArray(AudioObjectID id,
+ AudioObjectPropertySelector selector,
+ void **outData)
+{
+ return GetAudioPropertyArray(id, selector, kAudioObjectPropertyScopeGlobal, outData);
+}
+
+static OSStatus GetAudioPropertyString(AudioObjectID id,
+ AudioObjectPropertySelector selector,
+ char **outData)
+{
+ OSStatus err;
+ AudioObjectPropertyAddress property_address;
+ UInt32 i_param_size;
+ CFStringRef string;
+ CFIndex string_length;
+
+ property_address.mSelector = selector;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ i_param_size = sizeof(CFStringRef);
+ err = AudioObjectGetPropertyData(id, &property_address, 0, NULL, &i_param_size, &string);
+ if (err != noErr)
+ return err;
+
+ string_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(string),
+ kCFStringEncodingASCII);
+ *outData = malloc(string_length + 1);
+ CFStringGetCString(string, *outData, string_length + 1, kCFStringEncodingASCII);
+
+ CFRelease(string);
+
+ return err;
+}
+
+static OSStatus SetAudioProperty(AudioObjectID id,
+ AudioObjectPropertySelector selector,
+ UInt32 inDataSize, void *inData)
+{
+ AudioObjectPropertyAddress property_address;
+
+ property_address.mSelector = selector;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ return AudioObjectSetPropertyData(id, &property_address, 0, NULL, inDataSize, inData);
+}
+
+static Boolean IsAudioPropertySettable(AudioObjectID id,
+ AudioObjectPropertySelector selector,
+ Boolean *outData)
+{
+ AudioObjectPropertyAddress property_address;
+
+ property_address.mSelector = selector;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ return AudioObjectIsPropertySettable(id, &property_address, outData);
+}
+
+static int AudioDeviceSupportsDigital( AudioDeviceID i_dev_id );
+static int AudioStreamSupportsDigital( AudioStreamID i_stream_id );
+static int OpenSPDIF(void);
+static int AudioStreamChangeFormat( AudioStreamID i_stream_id, AudioStreamBasicDescription change_format );
+static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
+ const AudioTimeStamp * inNow,
+ const void * inInputData,
+ const AudioTimeStamp * inInputTime,
+ AudioBufferList * outOutputData,
+ const AudioTimeStamp * inOutputTime,
+ void * threadGlobals );
+static OSStatus StreamListener( AudioObjectID inObjectID,
+ UInt32 inNumberAddresses,
+ const AudioObjectPropertyAddress inAddresses[],
+ void *inClientData );
+static OSStatus DeviceListener( AudioObjectID inObjectID,
+ UInt32 inNumberAddresses,
+ const AudioObjectPropertyAddress inAddresses[],
+ void *inClientData );
+
+static void print_help(void)
+{
+ OSStatus err;
+ UInt32 i_param_size;
+ int num_devices;
+ AudioDeviceID *devids;
+ char *device_name;
+
+ mp_msg(MSGT_AO, MSGL_FATAL,
+ "\n-ao coreaudio commandline help:\n"
+ "Example: mpv -ao coreaudio:device_id=266\n"
+ " open Core Audio with output device ID 266.\n"
+ "\nOptions:\n"
+ " device_id\n"
+ " ID of output device to use (0 = default device)\n"
+ " help\n"
+ " This help including list of available devices.\n"
+ "\n"
+ "Available output devices:\n");
+
+ i_param_size = GetGlobalAudioPropertyArray(kAudioObjectSystemObject, kAudioHardwarePropertyDevices, (void **)&devids);
+
+ if (!i_param_size) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "Failed to get list of output devices.\n");
+ return;
+ }
+
+ num_devices = i_param_size / sizeof(AudioDeviceID);
+
+ for (int i = 0; i < num_devices; ++i) {
+ err = GetAudioPropertyString(devids[i], kAudioObjectPropertyName, &device_name);
+
+ if (err == noErr) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "%s (id: %"PRIu32")\n", device_name, devids[i]);
+ free(device_name);
+ } else
+ mp_msg(MSGT_AO, MSGL_FATAL, "Unknown (id: %"PRIu32")\n", devids[i]);
+ }
+
+ mp_msg(MSGT_AO, MSGL_FATAL, "\n");
+
+ free(devids);
+}
+
+static int init(int rate,int channels,int format,int flags)
+{
+AudioStreamBasicDescription inDesc;
+ComponentDescription desc;
+Component comp;
+AURenderCallbackStruct renderCallback;
+OSStatus err;
+UInt32 size, maxFrames, b_alive;
+char *psz_name;
+AudioDeviceID devid_def = 0;
+int device_id, display_help = 0;
+
+ const opt_t subopts[] = {
+ {"device_id", OPT_ARG_INT, &device_id, NULL},
+ {"help", OPT_ARG_BOOL, &display_help, NULL},
+ {NULL}
+ };
+
+ // set defaults
+ device_id = 0;
+
+ if (subopt_parse(ao_subdevice, subopts) != 0 || display_help) {
+ print_help();
+ if (!display_help)
+ return 0;
+ }
+
+ ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, channels, af_fmt2str_short(format), flags);
+
+ ao = calloc(1, sizeof(ao_coreaudio_t));
+
+ ao->i_selected_dev = 0;
+ ao->b_supports_digital = 0;
+ ao->b_digital = 0;
+ ao->b_muted = 0;
+ ao->b_stream_format_changed = 0;
+ ao->i_hog_pid = -1;
+ ao->i_stream_id = 0;
+ ao->i_stream_index = -1;
+ ao->b_revert = 0;
+ ao->b_changed_mixing = 0;
+
+ global_ao->per_application_mixer = true;
+ global_ao->no_persistent_volume = true;
+
+ if (device_id == 0) {
+ /* Find the ID of the default Device. */
+ err = GetAudioProperty(kAudioObjectSystemObject,
+ kAudioHardwarePropertyDefaultOutputDevice,
+ sizeof(UInt32), &devid_def);
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "could not get default audio device: [%4.4s]\n", (char *)&err);
+ goto err_out;
+ }
+ } else {
+ devid_def = device_id;
+ }
+
+ /* Retrieve the name of the device. */
+ err = GetAudioPropertyString(devid_def,
+ kAudioObjectPropertyName,
+ &psz_name);
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "could not get default audio device name: [%4.4s]\n", (char *)&err);
+ goto err_out;
+ }
+
+ ao_msg(MSGT_AO,MSGL_V, "got audio output device ID: %"PRIu32" Name: %s\n", devid_def, psz_name );
+
+ /* Probe whether device support S/PDIF stream output if input is AC3 stream. */
+ if (AF_FORMAT_IS_AC3(format)) {
+ if (AudioDeviceSupportsDigital(devid_def))
+ {
+ ao->b_supports_digital = 1;
+ }
+ ao_msg(MSGT_AO, MSGL_V,
+ "probe default audio output device about support for digital s/pdif output: %d\n",
+ ao->b_supports_digital );
+ }
+
+ free(psz_name);
+
+ // Save selected device id
+ ao->i_selected_dev = devid_def;
+
+ // Build Description for the input format
+ inDesc.mSampleRate=rate;
+ inDesc.mFormatID=ao->b_supports_digital ? kAudioFormat60958AC3 : kAudioFormatLinearPCM;
+ inDesc.mChannelsPerFrame=channels;
+ inDesc.mBitsPerChannel=af_fmt2bits(format);
+
+ if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) {
+ // float
+ inDesc.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked;
+ }
+ else if((format&AF_FORMAT_SIGN_MASK)==AF_FORMAT_SI) {
+ // signed int
+ inDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
+ }
+ else {
+ // unsigned int
+ inDesc.mFormatFlags = kAudioFormatFlagIsPacked;
+ }
+ if ((format & AF_FORMAT_END_MASK) == AF_FORMAT_BE)
+ inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+
+ inDesc.mFramesPerPacket = 1;
+ ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*channels*(inDesc.mBitsPerChannel/8);
+ print_format(MSGL_V, "source:",&inDesc);
+
+ if (ao->b_supports_digital)
+ {
+ b_alive = 1;
+ err = GetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertyDeviceIsAlive,
+ sizeof(UInt32), &b_alive);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "could not check whether device is alive: [%4.4s]\n", (char *)&err);
+ if (!b_alive)
+ ao_msg(MSGT_AO, MSGL_WARN, "device is not alive\n" );
+
+ /* S/PDIF output need device in HogMode. */
+ err = GetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertyHogMode,
+ sizeof(pid_t), &ao->i_hog_pid);
+ if (err != noErr)
+ {
+ /* This is not a fatal error. Some drivers simply don't support this property. */
+ ao_msg(MSGT_AO, MSGL_WARN, "could not check whether device is hogged: [%4.4s]\n",
+ (char *)&err);
+ ao->i_hog_pid = -1;
+ }
+
+ if (ao->i_hog_pid != -1 && ao->i_hog_pid != getpid())
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "Selected audio device is exclusively in use by another program.\n" );
+ goto err_out;
+ }
+ ao->stream_format = inDesc;
+ return OpenSPDIF();
+ }
+
+ /* original analog output code */
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = (device_id == 0) ? kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ comp = FindNextComponent(NULL, &desc); //Finds an component that meets the desc spec's
+ if (comp == NULL) {
+ ao_msg(MSGT_AO, MSGL_WARN, "Unable to find Output Unit component\n");
+ goto err_out;
+ }
+
+ err = OpenAComponent(comp, &(ao->theOutputUnit)); //gains access to the services provided by the component
+ if (err) {
+ ao_msg(MSGT_AO, MSGL_WARN, "Unable to open Output Unit component: [%4.4s]\n", (char *)&err);
+ goto err_out;
+ }
+
+ // Initialize AudioUnit
+ err = AudioUnitInitialize(ao->theOutputUnit);
+ if (err) {
+ ao_msg(MSGT_AO, MSGL_WARN, "Unable to initialize Output Unit component: [%4.4s]\n", (char *)&err);
+ goto err_out1;
+ }
+
+ size = sizeof(AudioStreamBasicDescription);
+ err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &inDesc, size);
+
+ if (err) {
+ ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the input format: [%4.4s]\n", (char *)&err);
+ goto err_out2;
+ }
+
+ size = sizeof(UInt32);
+ err = AudioUnitGetProperty(ao->theOutputUnit, kAudioDevicePropertyBufferSize, kAudioUnitScope_Input, 0, &maxFrames, &size);
+
+ if (err)
+ {
+ ao_msg(MSGT_AO,MSGL_WARN, "AudioUnitGetProperty returned [%4.4s] when getting kAudioDevicePropertyBufferSize\n", (char *)&err);
+ goto err_out2;
+ }
+
+ //Set the Current Device to the Default Output Unit.
+ err = AudioUnitSetProperty(ao->theOutputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &ao->i_selected_dev, sizeof(ao->i_selected_dev));
+
+ ao->chunk_size = maxFrames;//*inDesc.mBytesPerFrame;
+
+ ao_data.samplerate = inDesc.mSampleRate;
+ ao_data.channels = inDesc.mChannelsPerFrame;
+ ao_data.bps = ao_data.samplerate * inDesc.mBytesPerFrame;
+ ao_data.outburst = ao->chunk_size;
+ ao_data.buffersize = ao_data.bps;
+
+ ao->num_chunks = (ao_data.bps+ao->chunk_size-1)/ao->chunk_size;
+ ao->buffer_len = ao->num_chunks * ao->chunk_size;
+ ao->buffer = av_fifo_alloc(ao->buffer_len);
+
+ ao_msg(MSGT_AO,MSGL_V, "using %5d chunks of %d bytes (buffer len %d bytes)\n", (int)ao->num_chunks, (int)ao->chunk_size, (int)ao->buffer_len);
+
+ renderCallback.inputProc = theRenderProc;
+ renderCallback.inputProcRefCon = 0;
+ err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct));
+ if (err) {
+ ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the render callback: [%4.4s]\n", (char *)&err);
+ goto err_out2;
+ }
+
+ reset();
+
+ return CONTROL_OK;
+
+err_out2:
+ AudioUnitUninitialize(ao->theOutputUnit);
+err_out1:
+ CloseComponent(ao->theOutputUnit);
+err_out:
+ av_fifo_free(ao->buffer);
+ free(ao);
+ ao = NULL;
+ return CONTROL_FALSE;
+}
+
+/*****************************************************************************
+ * Setup a encoded digital stream (SPDIF)
+ *****************************************************************************/
+static int OpenSPDIF(void)
+{
+ OSStatus err = noErr;
+ UInt32 i_param_size, b_mix = 0;
+ Boolean b_writeable = 0;
+ AudioStreamID *p_streams = NULL;
+ int i, i_streams = 0;
+ AudioObjectPropertyAddress property_address;
+
+ /* Start doing the SPDIF setup process. */
+ ao->b_digital = 1;
+
+ /* Hog the device. */
+ ao->i_hog_pid = getpid() ;
+
+ err = SetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertyHogMode,
+ sizeof(ao->i_hog_pid), &ao->i_hog_pid);
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "failed to set hogmode: [%4.4s]\n", (char *)&err);
+ ao->i_hog_pid = -1;
+ goto err_out;
+ }
+
+ property_address.mSelector = kAudioDevicePropertySupportsMixing;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ /* Set mixable to false if we are allowed to. */
+ if (AudioObjectHasProperty(ao->i_selected_dev, &property_address)) {
+ /* Set mixable to false if we are allowed to. */
+ err = IsAudioPropertySettable(ao->i_selected_dev,
+ kAudioDevicePropertySupportsMixing,
+ &b_writeable);
+ err = GetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertySupportsMixing,
+ sizeof(UInt32), &b_mix);
+ if (err == noErr && b_writeable)
+ {
+ b_mix = 0;
+ err = SetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertySupportsMixing,
+ sizeof(UInt32), &b_mix);
+ ao->b_changed_mixing = 1;
+ }
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "failed to set mixmode: [%4.4s]\n", (char *)&err);
+ goto err_out;
+ }
+ }
+
+ /* Get a list of all the streams on this device. */
+ i_param_size = GetAudioPropertyArray(ao->i_selected_dev,
+ kAudioDevicePropertyStreams,
+ kAudioDevicePropertyScopeOutput,
+ (void **)&p_streams);
+
+ if (!i_param_size) {
+ ao_msg(MSGT_AO, MSGL_WARN, "could not get number of streams.\n");
+ goto err_out;
+ }
+
+ i_streams = i_param_size / sizeof(AudioStreamID);
+
+ ao_msg(MSGT_AO, MSGL_V, "current device stream number: %d\n", i_streams);
+
+ for (i = 0; i < i_streams && ao->i_stream_index < 0; ++i)
+ {
+ /* Find a stream with a cac3 stream. */
+ AudioStreamRangedDescription *p_format_list = NULL;
+ int i_formats = 0, j = 0, b_digital = 0;
+
+ i_param_size = GetGlobalAudioPropertyArray(p_streams[i],
+ kAudioStreamPropertyAvailablePhysicalFormats,
+ (void **)&p_format_list);
+
+ if (!i_param_size) {
+ ao_msg(MSGT_AO, MSGL_WARN,
+ "Could not get number of stream formats.\n");
+ continue;
+ }
+
+ i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
+
+ /* Check if one of the supported formats is a digital format. */
+ for (j = 0; j < i_formats; ++j)
+ {
+ if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
+ p_format_list[j].mFormat.mFormatID == 'iac3' ||
+ p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
+ p_format_list[j].mFormat.mFormatID == kAudioFormatAC3)
+ {
+ b_digital = 1;
+ break;
+ }
+ }
+
+ if (b_digital)
+ {
+ /* If this stream supports a digital (cac3) format, then set it. */
+ int i_requested_rate_format = -1;
+ int i_current_rate_format = -1;
+ int i_backup_rate_format = -1;
+
+ ao->i_stream_id = p_streams[i];
+ ao->i_stream_index = i;
+
+ if (ao->b_revert == 0)
+ {
+ /* Retrieve the original format of this stream first if not done so already. */
+ err = GetAudioProperty(ao->i_stream_id,
+ kAudioStreamPropertyPhysicalFormat,
+ sizeof(ao->sfmt_revert), &ao->sfmt_revert);
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN,
+ "Could not retrieve the original stream format: [%4.4s]\n",
+ (char *)&err);
+ free(p_format_list);
+ continue;
+ }
+ ao->b_revert = 1;
+ }
+
+ for (j = 0; j < i_formats; ++j)
+ if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
+ p_format_list[j].mFormat.mFormatID == 'iac3' ||
+ p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
+ p_format_list[j].mFormat.mFormatID == kAudioFormatAC3)
+ {
+ if (p_format_list[j].mFormat.mSampleRate == ao->stream_format.mSampleRate)
+ {
+ i_requested_rate_format = j;
+ break;
+ }
+ if (p_format_list[j].mFormat.mSampleRate == ao->sfmt_revert.mSampleRate)
+ i_current_rate_format = j;
+ else if (i_backup_rate_format < 0 || p_format_list[j].mFormat.mSampleRate > p_format_list[i_backup_rate_format].mFormat.mSampleRate)
+ i_backup_rate_format = j;
+ }
+
+ if (i_requested_rate_format >= 0) /* We prefer to output at the samplerate of the original audio. */
+ ao->stream_format = p_format_list[i_requested_rate_format].mFormat;
+ else if (i_current_rate_format >= 0) /* If not possible, we will try to use the current samplerate of the device. */
+ ao->stream_format = p_format_list[i_current_rate_format].mFormat;
+ else ao->stream_format = p_format_list[i_backup_rate_format].mFormat; /* And if we have to, any digital format will be just fine (highest rate possible). */
+ }
+ free(p_format_list);
+ }
+ free(p_streams);
+
+ if (ao->i_stream_index < 0)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN,
+ "Cannot find any digital output stream format when OpenSPDIF().\n");
+ goto err_out;
+ }
+
+ print_format(MSGL_V, "original stream format:", &ao->sfmt_revert);
+
+ if (!AudioStreamChangeFormat(ao->i_stream_id, ao->stream_format))
+ goto err_out;
+
+ property_address.mSelector = kAudioDevicePropertyDeviceHasChanged;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ err = AudioObjectAddPropertyListener(ao->i_selected_dev,
+ &property_address,
+ DeviceListener,
+ NULL);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceAddPropertyListener for kAudioDevicePropertyDeviceHasChanged failed: [%4.4s]\n", (char *)&err);
+
+
+ /* FIXME: If output stream is not native byte-order, we need change endian somewhere. */
+ /* Although there's no such case reported. */
+#if BYTE_ORDER == BIG_ENDIAN
+ if (!(ao->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian))
+#else
+ /* tell mplayer that we need a byteswap on AC3 streams, */
+ if (ao->stream_format.mFormatID & kAudioFormat60958AC3)
+ ao_data.format = AF_FORMAT_AC3_LE;
+
+ if (ao->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
+#endif
+ ao_msg(MSGT_AO, MSGL_WARN,
+ "Output stream has non-native byte order, digital output may fail.\n");
+
+ /* For ac3/dts, just use packet size 6144 bytes as chunk size. */
+ ao->chunk_size = ao->stream_format.mBytesPerPacket;
+
+ ao_data.samplerate = ao->stream_format.mSampleRate;
+ ao_data.channels = ao->stream_format.mChannelsPerFrame;
+ ao_data.bps = ao_data.samplerate * (ao->stream_format.mBytesPerPacket/ao->stream_format.mFramesPerPacket);
+ ao_data.outburst = ao->chunk_size;
+ ao_data.buffersize = ao_data.bps;
+
+ ao->num_chunks = (ao_data.bps+ao->chunk_size-1)/ao->chunk_size;
+ ao->buffer_len = ao->num_chunks * ao->chunk_size;
+ ao->buffer = av_fifo_alloc(ao->buffer_len);
+
+ ao_msg(MSGT_AO,MSGL_V, "using %5d chunks of %d bytes (buffer len %d bytes)\n", (int)ao->num_chunks, (int)ao->chunk_size, (int)ao->buffer_len);
+
+
+ /* Create IOProc callback. */
+ err = AudioDeviceCreateIOProcID(ao->i_selected_dev,
+ (AudioDeviceIOProc)RenderCallbackSPDIF,
+ (void *)ao,
+ &ao->renderCallback);
+
+ if (err != noErr || ao->renderCallback == NULL)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceAddIOProc failed: [%4.4s]\n", (char *)&err);
+ goto err_out1;
+ }
+
+ reset();
+
+ return CONTROL_TRUE;
+
+err_out1:
+ if (ao->b_revert)
+ AudioStreamChangeFormat(ao->i_stream_id, ao->sfmt_revert);
+err_out:
+ if (ao->b_changed_mixing && ao->sfmt_revert.mFormatID != kAudioFormat60958AC3)
+ {
+ int b_mix = 1;
+ err = SetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertySupportsMixing,
+ sizeof(int), &b_mix);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "failed to set mixmode: [%4.4s]\n",
+ (char *)&err);
+ }
+ if (ao->i_hog_pid == getpid())
+ {
+ ao->i_hog_pid = -1;
+ err = SetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertyHogMode,
+ sizeof(ao->i_hog_pid), &ao->i_hog_pid);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "Could not release hogmode: [%4.4s]\n",
+ (char *)&err);
+ }
+ av_fifo_free(ao->buffer);
+ free(ao);
+ ao = NULL;
+ return CONTROL_FALSE;
+}
+
+/*****************************************************************************
+ * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
+ *****************************************************************************/
+static int AudioDeviceSupportsDigital( AudioDeviceID i_dev_id )
+{
+ UInt32 i_param_size = 0;
+ AudioStreamID *p_streams = NULL;
+ int i = 0, i_streams = 0;
+ int b_return = CONTROL_FALSE;
+
+ /* Retrieve all the output streams. */
+ i_param_size = GetAudioPropertyArray(i_dev_id,
+ kAudioDevicePropertyStreams,
+ kAudioDevicePropertyScopeOutput,
+ (void **)&p_streams);
+
+ if (!i_param_size) {
+ ao_msg(MSGT_AO, MSGL_WARN, "could not get number of streams.\n");
+ return CONTROL_FALSE;
+ }
+
+ i_streams = i_param_size / sizeof(AudioStreamID);
+
+ for (i = 0; i < i_streams; ++i)
+ {
+ if (AudioStreamSupportsDigital(p_streams[i]))
+ b_return = CONTROL_OK;
+ }
+
+ free(p_streams);
+ return b_return;
+}
+
+/*****************************************************************************
+ * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
+ *****************************************************************************/
+static int AudioStreamSupportsDigital( AudioStreamID i_stream_id )
+{
+ UInt32 i_param_size;
+ AudioStreamRangedDescription *p_format_list = NULL;
+ int i, i_formats, b_return = CONTROL_FALSE;
+
+ /* Retrieve all the stream formats supported by each output stream. */
+ i_param_size = GetGlobalAudioPropertyArray(i_stream_id,
+ kAudioStreamPropertyAvailablePhysicalFormats,
+ (void **)&p_format_list);
+
+ if (!i_param_size) {
+ ao_msg(MSGT_AO, MSGL_WARN, "Could not get number of stream formats.\n");
+ return CONTROL_FALSE;
+ }
+
+ i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
+
+ for (i = 0; i < i_formats; ++i)
+ {
+ print_format(MSGL_V, "supported format:", &(p_format_list[i].mFormat));
+
+ if (p_format_list[i].mFormat.mFormatID == 'IAC3' ||
+ p_format_list[i].mFormat.mFormatID == 'iac3' ||
+ p_format_list[i].mFormat.mFormatID == kAudioFormat60958AC3 ||
+ p_format_list[i].mFormat.mFormatID == kAudioFormatAC3)
+ b_return = CONTROL_OK;
+ }
+
+ free(p_format_list);
+ return b_return;
+}
+
+/*****************************************************************************
+ * AudioStreamChangeFormat: Change i_stream_id to change_format
+ *****************************************************************************/
+static int AudioStreamChangeFormat( AudioStreamID i_stream_id, AudioStreamBasicDescription change_format )
+{
+ OSStatus err = noErr;
+ int i;
+ AudioObjectPropertyAddress property_address;
+
+ static volatile int stream_format_changed;
+ stream_format_changed = 0;
+
+ print_format(MSGL_V, "setting stream format:", &change_format);
+
+ /* Install the callback. */
+ property_address.mSelector = kAudioStreamPropertyPhysicalFormat;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mElement = kAudioObjectPropertyElementMaster;
+
+ err = AudioObjectAddPropertyListener(i_stream_id,
+ &property_address,
+ StreamListener,
+ (void *)&stream_format_changed);
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioStreamAddPropertyListener failed: [%4.4s]\n", (char *)&err);
+ return CONTROL_FALSE;
+ }
+
+ /* Change the format. */
+ err = SetAudioProperty(i_stream_id,
+ kAudioStreamPropertyPhysicalFormat,
+ sizeof(AudioStreamBasicDescription), &change_format);
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "could not set the stream format: [%4.4s]\n", (char *)&err);
+ return CONTROL_FALSE;
+ }
+
+ /* The AudioStreamSetProperty is not only asynchronious,
+ * it is also not Atomic, in its behaviour.
+ * Therefore we check 5 times before we really give up.
+ * FIXME: failing isn't actually implemented yet. */
+ for (i = 0; i < 5; ++i)
+ {
+ AudioStreamBasicDescription actual_format;
+ int j;
+ for (j = 0; !stream_format_changed && j < 50; ++j)
+ usec_sleep(10000);
+ if (stream_format_changed)
+ stream_format_changed = 0;
+ else
+ ao_msg(MSGT_AO, MSGL_V, "reached timeout\n" );
+
+ err = GetAudioProperty(i_stream_id,
+ kAudioStreamPropertyPhysicalFormat,
+ sizeof(AudioStreamBasicDescription), &actual_format);
+
+ print_format(MSGL_V, "actual format in use:", &actual_format);
+ if (actual_format.mSampleRate == change_format.mSampleRate &&
+ actual_format.mFormatID == change_format.mFormatID &&
+ actual_format.mFramesPerPacket == change_format.mFramesPerPacket)
+ {
+ /* The right format is now active. */
+ break;
+ }
+ /* We need to check again. */
+ }
+
+ /* Removing the property listener. */
+ err = AudioObjectRemovePropertyListener(i_stream_id,
+ &property_address,
+ StreamListener,
+ (void *)&stream_format_changed);
+ if (err != noErr)
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioStreamRemovePropertyListener failed: [%4.4s]\n", (char *)&err);
+ return CONTROL_FALSE;
+ }
+
+ return CONTROL_TRUE;
+}
+
+/*****************************************************************************
+ * RenderCallbackSPDIF: callback for SPDIF audio output
+ *****************************************************************************/
+static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
+ const AudioTimeStamp * inNow,
+ const void * inInputData,
+ const AudioTimeStamp * inInputTime,
+ AudioBufferList * outOutputData,
+ const AudioTimeStamp * inOutputTime,
+ void * threadGlobals )
+{
+ int amt = av_fifo_size(ao->buffer);
+ int req = outOutputData->mBuffers[ao->i_stream_index].mDataByteSize;
+
+ if (amt > req)
+ amt = req;
+ if (amt)
+ read_buffer(ao->b_muted ? NULL : (unsigned char *)outOutputData->mBuffers[ao->i_stream_index].mData, amt);
+
+ return noErr;
+}
+
+
+static int play(void* output_samples,int num_bytes,int flags)
+{
+ int wrote, b_digital;
+ SInt32 exit_reason;
+
+ // Check whether we need to reset the digital output stream.
+ if (ao->b_digital && ao->b_stream_format_changed)
+ {
+ ao->b_stream_format_changed = 0;
+ b_digital = AudioStreamSupportsDigital(ao->i_stream_id);
+ if (b_digital)
+ {
+ /* Current stream supports digital format output, let's set it. */
+ ao_msg(MSGT_AO, MSGL_V,
+ "Detected current stream supports digital, try to restore digital output...\n");
+
+ if (!AudioStreamChangeFormat(ao->i_stream_id, ao->stream_format))
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "Restoring digital output failed.\n");
+ }
+ else
+ {
+ ao_msg(MSGT_AO, MSGL_WARN, "Restoring digital output succeeded.\n");
+ reset();
+ }
+ }
+ else
+ ao_msg(MSGT_AO, MSGL_V, "Detected current stream does not support digital.\n");
+ }
+
+ wrote=write_buffer(output_samples, num_bytes);
+ audio_resume();
+
+ do {
+ exit_reason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, true);
+ } while (exit_reason == kCFRunLoopRunHandledSource);
+
+ return wrote;
+}
+
+/* set variables and buffer to initial state */
+static void reset(void)
+{
+ audio_pause();
+ av_fifo_reset(ao->buffer);
+}
+
+
+/* return available space */
+static int get_space(void)
+{
+ return ao->buffer_len - av_fifo_size(ao->buffer);
+}
+
+
+/* return delay until audio is played */
+static float get_delay(void)
+{
+ // inaccurate, should also contain the data buffered e.g. by the OS
+ return (float)av_fifo_size(ao->buffer)/(float)ao_data.bps;
+}
+
+
+/* unload plugin and deregister from coreaudio */
+static void uninit(int immed)
+{
+ OSStatus err = noErr;
+
+ if (!immed) {
+ long long timeleft=(1000000LL*av_fifo_size(ao->buffer))/ao_data.bps;
+ ao_msg(MSGT_AO,MSGL_DBG2, "%d bytes left @%d bps (%d usec)\n", av_fifo_size(ao->buffer), ao_data.bps, (int)timeleft);
+ usec_sleep((int)timeleft);
+ }
+
+ if (!ao->b_digital) {
+ AudioOutputUnitStop(ao->theOutputUnit);
+ AudioUnitUninitialize(ao->theOutputUnit);
+ CloseComponent(ao->theOutputUnit);
+ }
+ else {
+ /* Stop device. */
+ err = AudioDeviceStop(ao->i_selected_dev, ao->renderCallback);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceStop failed: [%4.4s]\n", (char *)&err);
+
+ /* Remove IOProc callback. */
+ err = AudioDeviceDestroyIOProcID(ao->i_selected_dev, ao->renderCallback);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceRemoveIOProc failed: [%4.4s]\n", (char *)&err);
+
+ if (ao->b_revert)
+ AudioStreamChangeFormat(ao->i_stream_id, ao->sfmt_revert);
+
+ if (ao->b_changed_mixing && ao->sfmt_revert.mFormatID != kAudioFormat60958AC3)
+ {
+ UInt32 b_mix;
+ Boolean b_writeable = 0;
+ /* Revert mixable to true if we are allowed to. */
+ err = IsAudioPropertySettable(ao->i_selected_dev,
+ kAudioDevicePropertySupportsMixing,
+ &b_writeable);
+ err = GetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertySupportsMixing,
+ sizeof(UInt32), &b_mix);
+ if (err == noErr && b_writeable)
+ {
+ b_mix = 1;
+ err = SetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertySupportsMixing,
+ sizeof(UInt32), &b_mix);
+ }
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "failed to set mixmode: [%4.4s]\n", (char *)&err);
+ }
+ if (ao->i_hog_pid == getpid())
+ {
+ ao->i_hog_pid = -1;
+ err = SetAudioProperty(ao->i_selected_dev,
+ kAudioDevicePropertyHogMode,
+ sizeof(ao->i_hog_pid), &ao->i_hog_pid);
+ if (err != noErr) ao_msg(MSGT_AO, MSGL_WARN, "Could not release hogmode: [%4.4s]\n", (char *)&err);
+ }
+ }
+
+ av_fifo_free(ao->buffer);
+ free(ao);
+ ao = NULL;
+}
+
+
+/* stop playing, keep buffers (for pause) */
+static void audio_pause(void)
+{
+ OSErr err=noErr;
+
+ /* Stop callback. */
+ if (!ao->b_digital)
+ {
+ err=AudioOutputUnitStop(ao->theOutputUnit);
+ if (err != noErr)
+ ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStop returned [%4.4s]\n", (char *)&err);
+ }
+ else
+ {
+ err = AudioDeviceStop(ao->i_selected_dev, ao->renderCallback);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceStop failed: [%4.4s]\n", (char *)&err);
+ }
+ ao->paused = 1;
+}
+
+
+/* resume playing, after audio_pause() */
+static void audio_resume(void)
+{
+ OSErr err=noErr;
+
+ if (!ao->paused)
+ return;
+
+ /* Start callback. */
+ if (!ao->b_digital)
+ {
+ err = AudioOutputUnitStart(ao->theOutputUnit);
+ if (err != noErr)
+ ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStart returned [%4.4s]\n", (char *)&err);
+ }
+ else
+ {
+ err = AudioDeviceStart(ao->i_selected_dev, ao->renderCallback);
+ if (err != noErr)
+ ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceStart failed: [%4.4s]\n", (char *)&err);
+ }
+ ao->paused = 0;
+}
+
+/*****************************************************************************
+ * StreamListener
+ *****************************************************************************/
+static OSStatus StreamListener( AudioObjectID inObjectID,
+ UInt32 inNumberAddresses,
+ const AudioObjectPropertyAddress inAddresses[],
+ void *inClientData )
+{
+ for (int i=0; i < inNumberAddresses; ++i)
+ {
+ if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) {
+ ao_msg(MSGT_AO, MSGL_WARN, "got notify kAudioStreamPropertyPhysicalFormat changed.\n");
+ if (inClientData)
+ *(volatile int *)inClientData = 1;
+ break;
+ }
+ }
+ return noErr;
+}
+
+static OSStatus DeviceListener( AudioObjectID inObjectID,
+ UInt32 inNumberAddresses,
+ const AudioObjectPropertyAddress inAddresses[],
+ void *inClientData )
+{
+ for (int i=0; i < inNumberAddresses; ++i)
+ {
+ if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceHasChanged) {
+ ao_msg(MSGT_AO, MSGL_WARN, "got notify kAudioDevicePropertyDeviceHasChanged.\n");
+ ao->b_stream_format_changed = 1;
+ break;
+ }
+ }
+ return noErr;
+}
diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c
new file mode 100644
index 0000000000..f2f44dd401
--- /dev/null
+++ b/audio/out/ao_dsound.c
@@ -0,0 +1,648 @@
+/*
+ * Windows DirectSound interface
+ *
+ * Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
+ *
+ * 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 verify/extend multichannel support
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#define DIRECTSOUND_VERSION 0x0600
+#include <dsound.h>
+#include <math.h>
+
+#include "config.h"
+#include "libaf/format.h"
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "mp_msg.h"
+#include "osdep/timer.h"
+#include "subopt-helper.h"
+
+
+static const ao_info_t info =
+{
+ "Windows DirectSound audio output",
+ "dsound",
+ "Gabor Szecsi <deje@miki.hu>",
+ ""
+};
+
+LIBAO_EXTERN(dsound)
+
+/**
+\todo use the definitions from the win32 api headers when they define these
+*/
+#define WAVE_FORMAT_IEEE_FLOAT 0x0003
+#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
+#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
+
+static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
+
+#define SPEAKER_FRONT_LEFT 0x1
+#define SPEAKER_FRONT_RIGHT 0x2
+#define SPEAKER_FRONT_CENTER 0x4
+#define SPEAKER_LOW_FREQUENCY 0x8
+#define SPEAKER_BACK_LEFT 0x10
+#define SPEAKER_BACK_RIGHT 0x20
+#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
+#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
+#define SPEAKER_BACK_CENTER 0x100
+#define SPEAKER_SIDE_LEFT 0x200
+#define SPEAKER_SIDE_RIGHT 0x400
+#define SPEAKER_TOP_CENTER 0x800
+#define SPEAKER_TOP_FRONT_LEFT 0x1000
+#define SPEAKER_TOP_FRONT_CENTER 0x2000
+#define SPEAKER_TOP_FRONT_RIGHT 0x4000
+#define SPEAKER_TOP_BACK_LEFT 0x8000
+#define SPEAKER_TOP_BACK_CENTER 0x10000
+#define SPEAKER_TOP_BACK_RIGHT 0x20000
+#define SPEAKER_RESERVED 0x80000000
+
+#if 0
+#define DSSPEAKER_HEADPHONE 0x00000001
+#define DSSPEAKER_MONO 0x00000002
+#define DSSPEAKER_QUAD 0x00000003
+#define DSSPEAKER_STEREO 0x00000004
+#define DSSPEAKER_SURROUND 0x00000005
+#define DSSPEAKER_5POINT1 0x00000006
+#endif
+
+#ifndef _WAVEFORMATEXTENSIBLE_
+typedef struct {
+ WAVEFORMATEX Format;
+ union {
+ WORD wValidBitsPerSample; /* bits of precision */
+ WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
+ WORD wReserved; /* If neither applies, set to zero. */
+ } Samples;
+ DWORD dwChannelMask; /* which channels are */
+ /* present in stream */
+ GUID SubFormat;
+} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
+#endif
+
+static const int channel_mask[] = {
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY,
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY,
+ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY
+};
+
+static HINSTANCE hdsound_dll = NULL; ///handle to the dll
+static LPDIRECTSOUND hds = NULL; ///direct sound object
+static LPDIRECTSOUNDBUFFER hdspribuf = NULL; ///primary direct sound buffer
+static LPDIRECTSOUNDBUFFER hdsbuf = NULL; ///secondary direct sound buffer (stream buffer)
+static int buffer_size = 0; ///size in bytes of the direct sound buffer
+static int write_offset = 0; ///offset of the write cursor in the direct sound buffer
+static int min_free_space = 0; ///if the free space is below this value get_space() will return 0
+ ///there will always be at least this amout of free space to prevent
+ ///get_space() from returning wrong values when buffer is 100% full.
+ ///will be replaced with nBlockAlign in init()
+static int underrun_check = 0; ///0 or last reported free space (underrun detection)
+static int device_num = 0; ///wanted device number
+static GUID device; ///guid of the device
+static int audio_volume;
+
+/***************************************************************************************/
+
+/**
+\brief output error message
+\param err error code
+\return string with the error message
+*/
+static char * dserr2str(int err)
+{
+ switch (err) {
+ case DS_OK: return "DS_OK";
+ case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
+ case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
+ case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
+ case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
+ case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
+ case DSERR_GENERIC: return "DSERR_GENERIC";
+ case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
+ case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
+ case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
+ case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
+ case DSERR_NODRIVER: return "DSERR_NODRIVER";
+ case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
+ case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
+ case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
+ case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
+ case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
+ case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
+ case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
+ default: return "unknown";
+ }
+}
+
+/**
+\brief uninitialize direct sound
+*/
+static void UninitDirectSound(void)
+{
+ // finally release the DirectSound object
+ if (hds) {
+ IDirectSound_Release(hds);
+ hds = NULL;
+ }
+ // free DSOUND.DLL
+ if (hdsound_dll) {
+ FreeLibrary(hdsound_dll);
+ hdsound_dll = NULL;
+ }
+ mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound uninitialized\n");
+}
+
+/**
+\brief print the commandline help
+*/
+static void print_help(void)
+{
+ mp_msg(MSGT_AO, MSGL_FATAL,
+ "\n-ao dsound commandline help:\n"
+ "Example: mpv -ao dsound:device=1\n"
+ " sets 1st device\n"
+ "\nOptions:\n"
+ " device=<device-number>\n"
+ " Sets device number, use -v to get a list\n");
+}
+
+
+/**
+\brief enumerate direct sound devices
+\return TRUE to continue with the enumeration
+*/
+static BOOL CALLBACK DirectSoundEnum(LPGUID guid,LPCSTR desc,LPCSTR module,LPVOID context)
+{
+ int* device_index=context;
+ mp_msg(MSGT_AO, MSGL_V,"%i %s ",*device_index,desc);
+ if(device_num==*device_index){
+ mp_msg(MSGT_AO, MSGL_V,"<--");
+ if(guid){
+ memcpy(&device,guid,sizeof(GUID));
+ }
+ }
+ mp_msg(MSGT_AO, MSGL_V,"\n");
+ (*device_index)++;
+ return TRUE;
+}
+
+
+/**
+\brief initilize direct sound
+\return 0 if error, 1 if ok
+*/
+static int InitDirectSound(void)
+{
+ DSCAPS dscaps;
+
+ // initialize directsound
+ HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
+ HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
+ int device_index=0;
+ const opt_t subopts[] = {
+ {"device", OPT_ARG_INT, &device_num,NULL},
+ {NULL}
+ };
+ if (subopt_parse(ao_subdevice, subopts) != 0) {
+ print_help();
+ return 0;
+ }
+
+ hdsound_dll = LoadLibrary("DSOUND.DLL");
+ if (hdsound_dll == NULL) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot load DSOUND.DLL\n");
+ return 0;
+ }
+ OurDirectSoundCreate = (void*)GetProcAddress(hdsound_dll, "DirectSoundCreate");
+ OurDirectSoundEnumerate = (void*)GetProcAddress(hdsound_dll, "DirectSoundEnumerateA");
+
+ if (OurDirectSoundCreate == NULL || OurDirectSoundEnumerate == NULL) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: GetProcAddress FAILED\n");
+ FreeLibrary(hdsound_dll);
+ return 0;
+ }
+
+ // Enumerate all directsound devices
+ mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Output Devices:\n");
+ OurDirectSoundEnumerate(DirectSoundEnum,&device_index);
+
+ // Create the direct sound object
+ if FAILED(OurDirectSoundCreate((device_num)?&device:NULL, &hds, NULL )) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create a DirectSound device\n");
+ FreeLibrary(hdsound_dll);
+ return 0;
+ }
+
+ /* Set DirectSound Cooperative level, ie what control we want over Windows
+ * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
+ * settings of the primary buffer, but also that only the sound of our
+ * application will be hearable when it will have the focus.
+ * !!! (this is not really working as intended yet because to set the
+ * cooperative level you need the window handle of your application, and
+ * I don't know of any easy way to get it. Especially since we might play
+ * sound without any video, and so what window handle should we use ???
+ * The hack for now is to use the Desktop window handle - it seems to be
+ * working */
+ if (IDirectSound_SetCooperativeLevel(hds, GetDesktopWindow(), DSSCL_EXCLUSIVE)) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot set direct sound cooperative level\n");
+ IDirectSound_Release(hds);
+ FreeLibrary(hdsound_dll);
+ return 0;
+ }
+ mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound initialized\n");
+
+ memset(&dscaps, 0, sizeof(DSCAPS));
+ dscaps.dwSize = sizeof(DSCAPS);
+ if (DS_OK == IDirectSound_GetCaps(hds, &dscaps)) {
+ if (dscaps.dwFlags & DSCAPS_EMULDRIVER) mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
+ } else {
+ mp_msg(MSGT_AO, MSGL_V, "ao_dsound: cannot get device capabilities\n");
+ }
+
+ return 1;
+}
+
+/**
+\brief destroy the direct sound buffer
+*/
+static void DestroyBuffer(void)
+{
+ if (hdsbuf) {
+ IDirectSoundBuffer_Release(hdsbuf);
+ hdsbuf = NULL;
+ }
+ if (hdspribuf) {
+ IDirectSoundBuffer_Release(hdspribuf);
+ hdspribuf = NULL;
+ }
+}
+
+/**
+\brief fill sound buffer
+\param data pointer to the sound data to copy
+\param len length of the data to copy in bytes
+\return number of copyed bytes
+*/
+static int write_buffer(unsigned char *data, int len)
+{
+ HRESULT res;
+ LPVOID lpvPtr1;
+ DWORD dwBytes1;
+ LPVOID lpvPtr2;
+ DWORD dwBytes2;
+
+ underrun_check = 0;
+
+ // Lock the buffer
+ res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+ // If the buffer was lost, restore and retry lock.
+ if (DSERR_BUFFERLOST == res)
+ {
+ IDirectSoundBuffer_Restore(hdsbuf);
+ res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+ }
+
+
+ if (SUCCEEDED(res))
+ {
+ if( (ao_data.channels == 6) && !AF_FORMAT_IS_AC3(ao_data.format) ) {
+ // reorder channels while writing to pointers.
+ // it's this easy because buffer size and len are always
+ // aligned to multiples of channels*bytespersample
+ // there's probably some room for speed improvements here
+ const int chantable[6] = {0, 1, 4, 5, 2, 3}; // reorder "matrix"
+ int i, j;
+ int numsamp,sampsize;
+
+ sampsize = af_fmt2bits(ao_data.format)>>3; // bytes per sample
+ numsamp = dwBytes1 / (ao_data.channels * sampsize); // number of samples for each channel in this buffer
+
+ for( i = 0; i < numsamp; i++ ) for( j = 0; j < ao_data.channels; j++ ) {
+ memcpy((char*)lpvPtr1+(i*ao_data.channels*sampsize)+(chantable[j]*sampsize),data+(i*ao_data.channels*sampsize)+(j*sampsize),sampsize);
+ }
+
+ if (NULL != lpvPtr2 )
+ {
+ numsamp = dwBytes2 / (ao_data.channels * sampsize);
+ for( i = 0; i < numsamp; i++ ) for( j = 0; j < ao_data.channels; j++ ) {
+ memcpy((char*)lpvPtr2+(i*ao_data.channels*sampsize)+(chantable[j]*sampsize),data+dwBytes1+(i*ao_data.channels*sampsize)+(j*sampsize),sampsize);
+ }
+ }
+
+ write_offset+=dwBytes1+dwBytes2;
+ if(write_offset>=buffer_size)write_offset=dwBytes2;
+ } else {
+ // Write to pointers without reordering.
+ memcpy(lpvPtr1,data,dwBytes1);
+ if (NULL != lpvPtr2 )memcpy(lpvPtr2,data+dwBytes1,dwBytes2);
+ write_offset+=dwBytes1+dwBytes2;
+ if(write_offset>=buffer_size)write_offset=dwBytes2;
+ }
+
+ // Release the data back to DirectSound.
+ res = IDirectSoundBuffer_Unlock(hdsbuf,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2);
+ if (SUCCEEDED(res))
+ {
+ // Success.
+ DWORD status;
+ IDirectSoundBuffer_GetStatus(hdsbuf, &status);
+ if (!(status & DSBSTATUS_PLAYING)){
+ res = IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
+ }
+ return dwBytes1+dwBytes2;
+ }
+ }
+ // Lock, Unlock, or Restore failed.
+ return 0;
+}
+
+/***************************************************************************************/
+
+/**
+\brief handle control commands
+\param cmd command
+\param arg argument
+\return CONTROL_OK or -1 in case the command can't be handled
+*/
+static int control(int cmd, void *arg)
+{
+ DWORD volume;
+ switch (cmd) {
+ case AOCONTROL_GET_VOLUME: {
+ ao_control_vol_t* vol = (ao_control_vol_t*)arg;
+ vol->left = vol->right = audio_volume;
+ return CONTROL_OK;
+ }
+ case AOCONTROL_SET_VOLUME: {
+ ao_control_vol_t* vol = (ao_control_vol_t*)arg;
+ volume = audio_volume = vol->right;
+ if (volume < 1)
+ volume = 1;
+ volume = (DWORD)(log10(volume) * 5000.0) - 10000;
+ IDirectSoundBuffer_SetVolume(hdsbuf, volume);
+ return CONTROL_OK;
+ }
+ }
+ return -1;
+}
+
+/**
+\brief setup sound device
+\param rate samplerate
+\param channels number of channels
+\param format format
+\param flags unused
+\return 1=success 0=fail
+*/
+static int init(int rate, int channels, int format, int flags)
+{
+ int res;
+ if (!InitDirectSound()) return 0;
+
+ global_ao->no_persistent_volume = true;
+ audio_volume = 100;
+
+ // ok, now create the buffers
+ WAVEFORMATEXTENSIBLE wformat;
+ DSBUFFERDESC dsbpridesc;
+ DSBUFFERDESC dsbdesc;
+
+ //check if the channel count and format is supported in general
+ if (channels > 6) {
+ UninitDirectSound();
+ mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n");
+ return 0;
+ }
+
+ if (AF_FORMAT_IS_AC3(format))
+ format = AF_FORMAT_AC3_NE;
+ switch(format){
+ case AF_FORMAT_AC3_NE:
+ case AF_FORMAT_S24_LE:
+ case AF_FORMAT_S16_LE:
+ case AF_FORMAT_U8:
+ break;
+ default:
+ mp_msg(MSGT_AO, MSGL_V,"ao_dsound: format %s not supported defaulting to Signed 16-bit Little-Endian\n",af_fmt2str_short(format));
+ format=AF_FORMAT_S16_LE;
+ }
+ //fill global ao_data
+ ao_data.channels = channels;
+ ao_data.samplerate = rate;
+ ao_data.format = format;
+ ao_data.bps = channels * rate * (af_fmt2bits(format)>>3);
+ if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec
+ mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format));
+ mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, ao_data.buffersize / ao_data.bps * 1000);
+
+ //fill waveformatex
+ ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
+ wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
+ wformat.Format.nChannels = channels;
+ wformat.Format.nSamplesPerSec = rate;
+ if (AF_FORMAT_IS_AC3(format)) {
+ wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
+ wformat.Format.wBitsPerSample = 16;
+ wformat.Format.nBlockAlign = 4;
+ } else {
+ wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
+ wformat.Format.wBitsPerSample = af_fmt2bits(format);
+ wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
+ }
+
+ // fill in primary sound buffer descriptor
+ memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC));
+ dsbpridesc.dwSize = sizeof(DSBUFFERDESC);
+ dsbpridesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
+ dsbpridesc.dwBufferBytes = 0;
+ dsbpridesc.lpwfxFormat = NULL;
+
+
+ // fill in the secondary sound buffer (=stream buffer) descriptor
+ memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
+ dsbdesc.dwSize = sizeof(DSBUFFERDESC);
+ dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
+ | DSBCAPS_GLOBALFOCUS /** Allows background playing */
+ | DSBCAPS_CTRLVOLUME; /** volume control enabled */
+
+ if (channels > 2) {
+ wformat.dwChannelMask = channel_mask[channels - 3];
+ wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
+ // Needed for 5.1 on emu101k - shit soundblaster
+ dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
+ }
+ wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign;
+
+ dsbdesc.dwBufferBytes = ao_data.buffersize;
+ dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
+ buffer_size = dsbdesc.dwBufferBytes;
+ write_offset = 0;
+ min_free_space = wformat.Format.nBlockAlign;
+ ao_data.outburst = wformat.Format.nBlockAlign * 512;
+
+ // create primary buffer and set its format
+
+ res = IDirectSound_CreateSoundBuffer( hds, &dsbpridesc, &hdspribuf, NULL );
+ if ( res != DS_OK ) {
+ UninitDirectSound();
+ mp_msg(MSGT_AO, MSGL_ERR,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res));
+ return 0;
+ }
+ res = IDirectSoundBuffer_SetFormat( hdspribuf, (WAVEFORMATEX *)&wformat );
+ if ( res != DS_OK ) mp_msg(MSGT_AO, MSGL_WARN,"ao_dsound: cannot set primary buffer format (%s), using standard setting (bad quality)", dserr2str(res));
+
+ mp_msg(MSGT_AO, MSGL_V, "ao_dsound: primary buffer created\n");
+
+ // now create the stream buffer
+
+ res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
+ if (res != DS_OK) {
+ if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
+ // Try without DSBCAPS_LOCHARDWARE
+ dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
+ res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
+ }
+ if (res != DS_OK) {
+ UninitDirectSound();
+ mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res));
+ return 0;
+ }
+ }
+ mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary (stream)buffer created\n");
+ return 1;
+}
+
+
+
+/**
+\brief stop playing and empty buffers (for seeking/pause)
+*/
+static void reset(void)
+{
+ IDirectSoundBuffer_Stop(hdsbuf);
+ // reset directsound buffer
+ IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
+ write_offset=0;
+ underrun_check=0;
+}
+
+/**
+\brief stop playing, keep buffers (for pause)
+*/
+static void audio_pause(void)
+{
+ IDirectSoundBuffer_Stop(hdsbuf);
+}
+
+/**
+\brief resume playing, after audio_pause()
+*/
+static void audio_resume(void)
+{
+ IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
+}
+
+/**
+\brief close audio device
+\param immed stop playback immediately
+*/
+static void uninit(int immed)
+{
+ if (!immed)
+ usec_sleep(get_delay() * 1000000);
+ reset();
+
+ DestroyBuffer();
+ UninitDirectSound();
+}
+
+// return exact number of free (safe to write) bytes
+static int check_free_buffer_size(void)
+{
+ int space;
+ DWORD play_offset;
+ IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
+ space=buffer_size-(write_offset-play_offset);
+ // | | <-- const --> | | |
+ // buffer start play_cursor write_cursor write_offset buffer end
+ // play_cursor is the actual postion of the play cursor
+ // write_cursor is the position after which it is assumed to be save to write data
+ // write_offset is the postion where we actually write the data to
+ if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
+ // Check for buffer underruns. An underrun happens if DirectSound
+ // started to play old data beyond the current write_offset. Detect this
+ // by checking whether the free space shrinks, even though no data was
+ // written (i.e. no write_buffer). Doesn't always work, but the only
+ // reason we need this is to deal with the situation when playback ends,
+ // and the buffer is only half-filled.
+ if (space < underrun_check) {
+ // there's no useful data in the buffers
+ space = buffer_size;
+ reset();
+ }
+ underrun_check = space;
+ return space;
+}
+
+/**
+\brief find out how many bytes can be written into the audio buffer without
+\return free space in bytes, has to return 0 if the buffer is almost full
+*/
+static int get_space(void)
+{
+ int space = check_free_buffer_size();
+ if(space < min_free_space)return 0;
+ return space-min_free_space;
+}
+
+/**
+\brief play 'len' bytes of 'data'
+\param data pointer to the data to play
+\param len size in bytes of the data buffer, gets rounded down to outburst*n
+\param flags currently unused
+\return number of played bytes
+*/
+static int play(void* data, int len, int flags)
+{
+ int space = check_free_buffer_size();
+ if(space < len) len = space;
+
+ if (!(flags & AOPLAY_FINAL_CHUNK))
+ len = (len / ao_data.outburst) * ao_data.outburst;
+ return write_buffer(data, len);
+}
+
+/**
+\brief get the delay between the first and last sample in the buffer
+\return delay in seconds
+*/
+static float get_delay(void)
+{
+ int space = check_free_buffer_size();
+ return (float)(buffer_size - space) / (float)ao_data.bps;
+}
diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c
new file mode 100644
index 0000000000..b30f99a14e
--- /dev/null
+++ b/audio/out/ao_jack.c
@@ -0,0 +1,361 @@
+/*
+ * JACK audio output driver for MPlayer
+ *
+ * Copyleft 2001 by Felix Bünemann (atmosfear@users.sf.net)
+ * and Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.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
+ * 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 <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "mp_msg.h"
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "libaf/format.h"
+#include "osdep/timer.h"
+#include "subopt-helper.h"
+
+#include "libavutil/fifo.h"
+
+#include <jack/jack.h>
+
+static const ao_info_t info =
+{
+ "JACK audio output",
+ "jack",
+ "Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>",
+ "based on ao_sdl.c"
+};
+
+LIBAO_EXTERN(jack)
+
+//! maximum number of channels supported, avoids lots of mallocs
+#define MAX_CHANS 8
+static jack_port_t *ports[MAX_CHANS];
+static int num_ports; ///< Number of used ports == number of channels
+static jack_client_t *client;
+static float jack_latency;
+static int estimate;
+static volatile int paused = 0; ///< set if paused
+static volatile int underrun = 0; ///< signals if an underrun occured
+
+static volatile float callback_interval = 0;
+static volatile float callback_time = 0;
+
+//! size of one chunk, if this is too small MPlayer will start to "stutter"
+//! after a short time of playback
+#define CHUNK_SIZE (16 * 1024)
+//! number of "virtual" chunks the buffer consists of
+#define NUM_CHUNKS 8
+#define BUFFSIZE (NUM_CHUNKS * CHUNK_SIZE)
+
+//! buffer for audio data
+static AVFifoBuffer *buffer;
+
+/**
+ * \brief insert len bytes into buffer
+ * \param data data to insert
+ * \param len length of data
+ * \return number of bytes inserted into buffer
+ *
+ * If there is not enough room, the buffer is filled up
+ */
+static int write_buffer(unsigned char* data, int len) {
+ int free = av_fifo_space(buffer);
+ if (len > free) len = free;
+ return av_fifo_generic_write(buffer, data, len, NULL);
+}
+
+static void silence(float **bufs, int cnt, int num_bufs);
+
+struct deinterleave {
+ float **bufs;
+ int num_bufs;
+ int cur_buf;
+ int pos;
+};
+
+static void deinterleave(void *info, void *src, int len) {
+ struct deinterleave *di = info;
+ float *s = src;
+ int i;
+ len /= sizeof(float);
+ for (i = 0; i < len; i++) {
+ di->bufs[di->cur_buf++][di->pos] = s[i];
+ if (di->cur_buf >= di->num_bufs) {
+ di->cur_buf = 0;
+ di->pos++;
+ }
+ }
+}
+
+/**
+ * \brief read data from buffer and splitting it into channels
+ * \param bufs num_bufs float buffers, each will contain the data of one channel
+ * \param cnt number of samples to read per channel
+ * \param num_bufs number of channels to split the data into
+ * \return number of samples read per channel, equals cnt unless there was too
+ * little data in the buffer
+ *
+ * Assumes the data in the buffer is of type float, the number of bytes
+ * read is res * num_bufs * sizeof(float), where res is the return value.
+ * If there is not enough data in the buffer remaining parts will be filled
+ * with silence.
+ */
+static int read_buffer(float **bufs, int cnt, int num_bufs) {
+ struct deinterleave di = {bufs, num_bufs, 0, 0};
+ int buffered = av_fifo_size(buffer);
+ if (cnt * sizeof(float) * num_bufs > buffered) {
+ silence(bufs, cnt, num_bufs);
+ cnt = buffered / sizeof(float) / num_bufs;
+ }
+ av_fifo_generic_read(buffer, &di, cnt * num_bufs * sizeof(float), deinterleave);
+ return cnt;
+}
+
+// end ring buffer stuff
+
+static int control(int cmd, void *arg) {
+ return CONTROL_UNKNOWN;
+}
+
+/**
+ * \brief fill the buffers with silence
+ * \param bufs num_bufs float buffers, each will contain the data of one channel
+ * \param cnt number of samples in each buffer
+ * \param num_bufs number of buffers
+ */
+static void silence(float **bufs, int cnt, int num_bufs) {
+ int i;
+ for (i = 0; i < num_bufs; i++)
+ memset(bufs[i], 0, cnt * sizeof(float));
+}
+
+/**
+ * \brief JACK Callback function
+ * \param nframes number of frames to fill into buffers
+ * \param arg unused
+ * \return currently always 0
+ *
+ * Write silence into buffers if paused or an underrun occured
+ */
+static int outputaudio(jack_nframes_t nframes, void *arg) {
+ float *bufs[MAX_CHANS];
+ int i;
+ for (i = 0; i < num_ports; i++)
+ bufs[i] = jack_port_get_buffer(ports[i], nframes);
+ if (paused || underrun)
+ silence(bufs, nframes, num_ports);
+ else
+ if (read_buffer(bufs, nframes, num_ports) < nframes)
+ underrun = 1;
+ if (estimate) {
+ float now = (float)GetTimer() / 1000000.0;
+ float diff = callback_time + callback_interval - now;
+ if ((diff > -0.002) && (diff < 0.002))
+ callback_time += callback_interval;
+ else
+ callback_time = now;
+ callback_interval = (float)nframes / (float)ao_data.samplerate;
+ }
+ return 0;
+}
+
+/**
+ * \brief print suboption usage help
+ */
+static void print_help (void)
+{
+ mp_msg (MSGT_AO, MSGL_FATAL,
+ "\n-ao jack commandline help:\n"
+ "Example: mpv -ao jack:port=myout\n"
+ " connects mpv to the jack ports named myout\n"
+ "\nOptions:\n"
+ " port=<port name>\n"
+ " Connects to the given ports instead of the default physical ones\n"
+ " name=<client name>\n"
+ " Client name to pass to JACK\n"
+ " estimate\n"
+ " Estimates the amount of data in buffers (experimental)\n"
+ " autostart\n"
+ " Automatically start JACK server if necessary\n"
+ );
+}
+
+static int init(int rate, int channels, int format, int flags) {
+ const char **matching_ports = NULL;
+ char *port_name = NULL;
+ char *client_name = NULL;
+ int autostart = 0;
+ const opt_t subopts[] = {
+ {"port", OPT_ARG_MSTRZ, &port_name, NULL},
+ {"name", OPT_ARG_MSTRZ, &client_name, NULL},
+ {"estimate", OPT_ARG_BOOL, &estimate, NULL},
+ {"autostart", OPT_ARG_BOOL, &autostart, NULL},
+ {NULL}
+ };
+ jack_options_t open_options = JackUseExactName;
+ int port_flags = JackPortIsInput;
+ int i;
+ estimate = 1;
+ if (subopt_parse(ao_subdevice, subopts) != 0) {
+ print_help();
+ return 0;
+ }
+ if (channels > MAX_CHANS) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] Invalid number of channels: %i\n", channels);
+ goto err_out;
+ }
+ if (!client_name) {
+ client_name = malloc(40);
+ sprintf(client_name, "mpv [%d]", getpid());
+ }
+ if (!autostart)
+ open_options |= JackNoStartServer;
+ client = jack_client_open(client_name, open_options, NULL);
+ if (!client) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] cannot open server\n");
+ goto err_out;
+ }
+ buffer = av_fifo_alloc(BUFFSIZE);
+ jack_set_process_callback(client, outputaudio, 0);
+
+ // list matching ports
+ if (!port_name)
+ port_flags |= JackPortIsPhysical;
+ matching_ports = jack_get_ports(client, port_name, NULL, port_flags);
+ if (!matching_ports || !matching_ports[0]) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] no physical ports available\n");
+ goto err_out;
+ }
+ i = 1;
+ while (matching_ports[i]) i++;
+ if (channels > i) channels = i;
+ num_ports = channels;
+
+ // create out output ports
+ for (i = 0; i < num_ports; i++) {
+ char pname[30];
+ snprintf(pname, 30, "out_%d", i);
+ ports[i] = jack_port_register(client, pname, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if (!ports[i]) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] not enough ports available\n");
+ goto err_out;
+ }
+ }
+ if (jack_activate(client)) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] activate failed\n");
+ goto err_out;
+ }
+ for (i = 0; i < num_ports; i++) {
+ if (jack_connect(client, jack_port_name(ports[i]), matching_ports[i])) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] connecting failed\n");
+ goto err_out;
+ }
+ }
+ rate = jack_get_sample_rate(client);
+ jack_latency = (float)(jack_port_get_total_latency(client, ports[0]) +
+ jack_get_buffer_size(client)) / (float)rate;
+ callback_interval = 0;
+
+ ao_data.channels = channels;
+ ao_data.samplerate = rate;
+ ao_data.format = AF_FORMAT_FLOAT_NE;
+ ao_data.bps = channels * rate * sizeof(float);
+ ao_data.buffersize = CHUNK_SIZE * NUM_CHUNKS;
+ ao_data.outburst = CHUNK_SIZE;
+ free(matching_ports);
+ free(port_name);
+ free(client_name);
+ return 1;
+
+err_out:
+ free(matching_ports);
+ free(port_name);
+ free(client_name);
+ if (client)
+ jack_client_close(client);
+ av_fifo_free(buffer);
+ buffer = NULL;
+ return 0;
+}
+
+// close audio device
+static void uninit(int immed) {
+ if (!immed)
+ usec_sleep(get_delay() * 1000 * 1000);
+ // HACK, make sure jack doesn't loop-output dirty buffers
+ reset();
+ usec_sleep(100 * 1000);
+ jack_client_close(client);
+ av_fifo_free(buffer);
+ buffer = NULL;
+}
+
+/**
+ * \brief stop playing and empty buffers (for seeking/pause)
+ */
+static void reset(void) {
+ paused = 1;
+ av_fifo_reset(buffer);
+ paused = 0;
+}
+
+/**
+ * \brief stop playing, keep buffers (for pause)
+ */
+static void audio_pause(void) {
+ paused = 1;
+}
+
+/**
+ * \brief resume playing, after audio_pause()
+ */
+static void audio_resume(void) {
+ paused = 0;
+}
+
+static int get_space(void) {
+ return av_fifo_space(buffer);
+}
+
+/**
+ * \brief write data into buffer and reset underrun flag
+ */
+static int play(void *data, int len, int flags) {
+ if (!(flags & AOPLAY_FINAL_CHUNK))
+ len -= len % ao_data.outburst;
+ underrun = 0;
+ return write_buffer(data, len);
+}
+
+static float get_delay(void) {
+ int buffered = av_fifo_size(buffer); // could be less
+ float in_jack = jack_latency;
+ if (estimate && callback_interval > 0) {
+ float elapsed = (float)GetTimer() / 1000000.0 - callback_time;
+ in_jack += callback_interval - elapsed;
+ if (in_jack < 0) in_jack = 0;
+ }
+ return (float)buffered / (float)ao_data.bps + in_jack;
+}
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
new file mode 100644
index 0000000000..ef76db2717
--- /dev/null
+++ b/audio/out/ao_lavc.c
@@ -0,0 +1,621 @@
+/*
+ * audio encoding using libavformat
+ * Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
+ * NOTE: this file is partially based on ao_pcm.c by Atmosfear
+ *
+ * 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 <libavutil/common.h>
+#include <libavutil/audioconvert.h>
+
+#include "config.h"
+#include "options.h"
+#include "mpcommon.h"
+#include "fmt-conversion.h"
+#include "libaf/format.h"
+#include "libaf/reorder_ch.h"
+#include "talloc.h"
+#include "audio_out.h"
+#include "mp_msg.h"
+
+#include "encode_lavc.h"
+
+static const char *sample_padding_signed = "\x00\x00\x00\x00";
+static const char *sample_padding_u8 = "\x80";
+static const char *sample_padding_float = "\x00\x00\x00\x00";
+
+struct priv {
+ uint8_t *buffer;
+ size_t buffer_size;
+ AVStream *stream;
+ int pcmhack;
+ int aframesize;
+ int aframecount;
+ int offset;
+ int offset_left;
+ int64_t savepts;
+ int framecount;
+ int64_t lastpts;
+ int sample_size;
+ const void *sample_padding;
+ double expected_next_pts;
+
+ AVRational worst_time_base;
+ int worst_time_base_is_stream;
+};
+
+// open & setup audio device
+static int init(struct ao *ao, char *params)
+{
+ struct priv *ac = talloc_zero(ao, struct priv);
+ const enum AVSampleFormat *sampleformat;
+ AVCodec *codec;
+
+ if (!encode_lavc_available(ao->encode_lavc_ctx)) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR,
+ "ao-lavc: the option -o (output file) must be specified\n");
+ return -1;
+ }
+
+ if (ac->stream) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: rejecting reinitialization\n");
+ return -1;
+ }
+
+ ac->stream = encode_lavc_alloc_stream(ao->encode_lavc_ctx,
+ AVMEDIA_TYPE_AUDIO);
+
+ if (!ac->stream) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: could not get a new audio stream\n");
+ return -1;
+ }
+
+ codec = encode_lavc_get_codec(ao->encode_lavc_ctx, ac->stream);
+
+ // ac->stream->time_base.num = 1;
+ // ac->stream->time_base.den = ao->samplerate;
+ // doing this breaks mpeg2ts in ffmpeg
+ // which doesn't properly force the time base to be 90000
+ // furthermore, ffmpeg.c doesn't do this either and works
+
+ ac->stream->codec->time_base.num = 1;
+ ac->stream->codec->time_base.den = ao->samplerate;
+
+ ac->stream->codec->sample_rate = ao->samplerate;
+ ac->stream->codec->channels = ao->channels;
+
+ ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
+
+ {
+ // first check if the selected format is somewhere in the list of
+ // supported formats by the codec
+ for (sampleformat = codec->sample_fmts;
+ sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
+ ++sampleformat) {
+ switch (*sampleformat) {
+ case AV_SAMPLE_FMT_U8:
+ if (ao->format == AF_FORMAT_U8)
+ goto out_search;
+ break;
+ case AV_SAMPLE_FMT_S16:
+ if (ao->format == AF_FORMAT_S16_BE)
+ goto out_search;
+ if (ao->format == AF_FORMAT_S16_LE)
+ goto out_search;
+ break;
+ case AV_SAMPLE_FMT_S32:
+ if (ao->format == AF_FORMAT_S32_BE)
+ goto out_search;
+ if (ao->format == AF_FORMAT_S32_LE)
+ goto out_search;
+ break;
+ case AV_SAMPLE_FMT_FLT:
+ if (ao->format == AF_FORMAT_FLOAT_BE)
+ goto out_search;
+ if (ao->format == AF_FORMAT_FLOAT_LE)
+ goto out_search;
+ break;
+ default:
+ break;
+ }
+ }
+out_search:
+ ;
+ }
+
+ if (!sampleformat || *sampleformat == AV_SAMPLE_FMT_NONE) {
+ // if the selected format is not supported, we have to pick the first
+ // one we CAN support
+ // note: not needing to select endianness here, as the switch() below
+ // does that anyway for us
+ for (sampleformat = codec->sample_fmts;
+ sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
+ ++sampleformat) {
+ switch (*sampleformat) {
+ case AV_SAMPLE_FMT_U8:
+ ao->format = AF_FORMAT_U8;
+ goto out_takefirst;
+ case AV_SAMPLE_FMT_S16:
+ ao->format = AF_FORMAT_S16_NE;
+ goto out_takefirst;
+ case AV_SAMPLE_FMT_S32:
+ ao->format = AF_FORMAT_S32_NE;
+ goto out_takefirst;
+ case AV_SAMPLE_FMT_FLT:
+ ao->format = AF_FORMAT_FLOAT_NE;
+ goto out_takefirst;
+ default:
+ break;
+ }
+ }
+out_takefirst:
+ ;
+ }
+
+ switch (ao->format) {
+ // now that we have chosen a format, set up the fields for it, boldly
+ // switching endianness if needed (mplayer code will convert for us
+ // anyway, but ffmpeg always expects native endianness)
+ case AF_FORMAT_U8:
+ ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_U8;
+ ac->sample_size = 1;
+ ac->sample_padding = sample_padding_u8;
+ ao->format = AF_FORMAT_U8;
+ break;
+ default:
+ case AF_FORMAT_S16_BE:
+ case AF_FORMAT_S16_LE:
+ ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S16;
+ ac->sample_size = 2;
+ ac->sample_padding = sample_padding_signed;
+ ao->format = AF_FORMAT_S16_NE;
+ break;
+ case AF_FORMAT_S32_BE:
+ case AF_FORMAT_S32_LE:
+ ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S32;
+ ac->sample_size = 4;
+ ac->sample_padding = sample_padding_signed;
+ ao->format = AF_FORMAT_S32_NE;
+ break;
+ case AF_FORMAT_FLOAT_BE:
+ case AF_FORMAT_FLOAT_LE:
+ ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_FLT;
+ ac->sample_size = 4;
+ ac->sample_padding = sample_padding_float;
+ ao->format = AF_FORMAT_FLOAT_NE;
+ break;
+ }
+
+ ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
+
+ switch (ao->channels) {
+ case 1:
+ ac->stream->codec->channel_layout = AV_CH_LAYOUT_MONO;
+ break;
+ case 2:
+ ac->stream->codec->channel_layout = AV_CH_LAYOUT_STEREO;
+ break;
+ /* someone please check if these are what mplayer normally assumes
+ case 3:
+ ac->stream->codec->channel_layout = AV_CH_LAYOUT_SURROUND;
+ break;
+ case 4:
+ ac->stream->codec->channel_layout = AV_CH_LAYOUT_2_2;
+ break;
+ */
+ case 5:
+ ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT0;
+ break;
+ case 6:
+ ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT1;
+ break;
+ case 8:
+ ac->stream->codec->channel_layout = AV_CH_LAYOUT_7POINT1;
+ break;
+ default:
+ mp_msg(MSGT_ENCODE, MSGL_ERR,
+ "ao-lavc: unknown channel layout; hoping for the best\n");
+ break;
+ }
+
+ if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
+ return -1;
+
+ ac->pcmhack = 0;
+ if (ac->stream->codec->frame_size <= 1)
+ ac->pcmhack = av_get_bits_per_sample(ac->stream->codec->codec_id) / 8;
+
+ if (ac->pcmhack) {
+ ac->aframesize = 16384; // "enough"
+ ac->buffer_size = ac->aframesize * ac->pcmhack * ao->channels * 2 + 200;
+ } else {
+ ac->aframesize = ac->stream->codec->frame_size;
+ ac->buffer_size = ac->aframesize * ac->sample_size * ao->channels * 2 +
+ 200;
+ }
+ if (ac->buffer_size < FF_MIN_BUFFER_SIZE)
+ ac->buffer_size = FF_MIN_BUFFER_SIZE;
+ ac->buffer = talloc_size(ac, ac->buffer_size);
+
+ // enough frames for at least 0.25 seconds
+ ac->framecount = ceil(ao->samplerate * 0.25 / ac->aframesize);
+ // but at least one!
+ ac->framecount = FFMAX(ac->framecount, 1);
+
+ ac->savepts = MP_NOPTS_VALUE;
+ ac->lastpts = MP_NOPTS_VALUE;
+ ac->offset = ac->stream->codec->sample_rate *
+ encode_lavc_getoffset(ao->encode_lavc_ctx, ac->stream);
+ ac->offset_left = ac->offset;
+
+ //fill_ao_data:
+ ao->outburst = ac->aframesize * ac->sample_size * ao->channels *
+ ac->framecount;
+ ao->buffersize = ao->outburst * 2;
+ ao->bps = ao->channels * ao->samplerate * ac->sample_size;
+ ao->untimed = true;
+ ao->priv = ac;
+
+ return 0;
+}
+
+static void fill_with_padding(void *buf, int cnt, int sz, const void *padding)
+{
+ int i;
+ if (sz == 1) {
+ memset(buf, cnt, *(char *)padding);
+ return;
+ }
+ for (i = 0; i < cnt; ++i)
+ memcpy((char *) buf + i * sz, padding, sz);
+}
+
+// close audio device
+static int encode(struct ao *ao, double apts, void *data);
+static void uninit(struct ao *ao, bool cut_audio)
+{
+ struct priv *ac = ao->priv;
+ struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
+
+ if (!encode_lavc_start(ectx)) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "ao-lavc: not even ready to encode audio at end -> dropped");
+ return;
+ }
+
+ if (ac->buffer) {
+ double pts = ao->pts + ac->offset / (double) ao->samplerate;
+ if (ao->buffer.len > 0) {
+ void *paddingbuf = talloc_size(ao,
+ ac->aframesize * ao->channels * ac->sample_size);
+ memcpy(paddingbuf, ao->buffer.start, ao->buffer.len);
+ fill_with_padding((char *) paddingbuf + ao->buffer.len,
+ (ac->aframesize * ao->channels * ac->sample_size
+ - ao->buffer.len) / ac->sample_size,
+ ac->sample_size, ac->sample_padding);
+ encode(ao, pts, paddingbuf);
+ pts += ac->aframesize / (double) ao->samplerate;
+ talloc_free(paddingbuf);
+ ao->buffer.len = 0;
+ }
+ while (encode(ao, pts, NULL) > 0) ;
+ }
+
+ ao->priv = NULL;
+}
+
+// return: how many bytes can be played without blocking
+static int get_space(struct ao *ao)
+{
+ return ao->outburst;
+}
+
+// must get exactly ac->aframesize amount of data
+static int encode(struct ao *ao, double apts, void *data)
+{
+ AVFrame *frame;
+ AVPacket packet;
+ struct priv *ac = ao->priv;
+ struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
+ double realapts = ac->aframecount * (double) ac->aframesize /
+ ao->samplerate;
+ int status, gotpacket;
+
+ ac->aframecount++;
+ if (data && (ao->channels == 5 || ao->channels == 6 || ao->channels == 8)) {
+ reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
+ AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
+ ao->channels,
+ ac->aframesize * ao->channels, ac->sample_size);
+ }
+
+ if (data)
+ ectx->audio_pts_offset = realapts - apts;
+
+ av_init_packet(&packet);
+ packet.data = ac->buffer;
+ packet.size = ac->buffer_size;
+ if(data)
+ {
+ frame = avcodec_alloc_frame();
+ frame->nb_samples = ac->aframesize;
+ if(avcodec_fill_audio_frame(frame, ao->channels, ac->stream->codec->sample_fmt, data, ac->aframesize * ao->channels * ac->sample_size, 1))
+ {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error filling\n");
+ return -1;
+ }
+
+ if (ectx->options->rawts || ectx->options->copyts) {
+ // real audio pts
+ frame->pts = floor(apts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
+ } else {
+ // audio playback time
+ frame->pts = floor(realapts * ac->stream->codec->time_base.den / ac->stream->codec->time_base.num + 0.5);
+ }
+
+ int64_t frame_pts = av_rescale_q(frame->pts, ac->stream->codec->time_base, ac->worst_time_base);
+ if (ac->lastpts != MP_NOPTS_VALUE && frame_pts <= ac->lastpts) {
+ // this indicates broken video
+ // (video pts failing to increase fast enough to match audio)
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: audio frame pts went backwards "
+ "(%d <- %d), autofixed\n", (int)frame->pts,
+ (int)ac->lastpts);
+ frame_pts = ac->lastpts + 1;
+ frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, ac->stream->codec->time_base);
+ }
+ ac->lastpts = frame_pts;
+
+ frame->quality = ac->stream->codec->global_quality;
+ status = avcodec_encode_audio2(ac->stream->codec, &packet, frame, &gotpacket);
+
+ if (!status) {
+ if (ac->savepts == MP_NOPTS_VALUE)
+ ac->savepts = frame->pts;
+ }
+
+ avcodec_free_frame(&frame);
+ }
+ else
+ {
+ status = avcodec_encode_audio2(ac->stream->codec, &packet, NULL, &gotpacket);
+ }
+
+ if(status)
+ {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error encoding\n");
+ return -1;
+ }
+
+ if(!gotpacket)
+ return 0;
+
+ mp_msg(MSGT_ENCODE, MSGL_DBG2,
+ "ao-lavc: got pts %f (playback time: %f); out size: %d\n",
+ apts, realapts, packet.size);
+
+ encode_lavc_write_stats(ao->encode_lavc_ctx, ac->stream);
+
+ packet.stream_index = ac->stream->index;
+
+ // Do we need this at all? Better be safe than sorry...
+ if (packet.pts == AV_NOPTS_VALUE) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: encoder lost pts, why?\n");
+ if (ac->savepts != MP_NOPTS_VALUE)
+ packet.pts = ac->savepts;
+ }
+
+ if (packet.pts != AV_NOPTS_VALUE)
+ packet.pts = av_rescale_q(packet.pts, ac->stream->codec->time_base,
+ ac->stream->time_base);
+
+ if (packet.dts != AV_NOPTS_VALUE)
+ packet.dts = av_rescale_q(packet.dts, ac->stream->codec->time_base,
+ ac->stream->time_base);
+
+ if(packet.duration > 0)
+ packet.duration = av_rescale_q(packet.duration, ac->stream->codec->time_base,
+ ac->stream->time_base);
+
+ ac->savepts = MP_NOPTS_VALUE;
+
+ if (encode_lavc_write_frame(ao->encode_lavc_ctx, &packet) < 0) {
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error writing at %f %f/%f\n",
+ realapts, (double) ac->stream->time_base.num,
+ (double) ac->stream->time_base.den);
+ return -1;
+ }
+
+ return packet.size;
+}
+
+// plays 'len' bytes of 'data'
+// it should round it down to outburst*n
+// return: number of bytes played
+static int play(struct ao *ao, void *data, int len, int flags)
+{
+ struct priv *ac = ao->priv;
+ struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
+ int bufpos = 0;
+ int64_t ptsoffset;
+ void *paddingbuf = NULL;
+ double nextpts;
+ double pts = ao->pts;
+ double outpts;
+
+ len /= ac->sample_size * ao->channels;
+
+ if (!encode_lavc_start(ectx)) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "ao-lavc: not ready yet for encoding audio\n");
+ return 0;
+ }
+ if (pts == MP_NOPTS_VALUE) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: frame without pts, please report; synthesizing pts instead\n");
+ // synthesize pts from previous expected next pts
+ pts = ac->expected_next_pts;
+ }
+
+ if (ac->worst_time_base.den == 0) {
+ //if (ac->stream->codec->time_base.num / ac->stream->codec->time_base.den >= ac->stream->time_base.num / ac->stream->time_base.den)
+ if (ac->stream->codec->time_base.num * (double) ac->stream->time_base.den >=
+ ac->stream->time_base.num * (double) ac->stream->codec->time_base.den) {
+ mp_msg(MSGT_ENCODE, MSGL_V, "ao-lavc: NOTE: using codec time base "
+ "(%d/%d) for pts adjustment; the stream base (%d/%d) is "
+ "not worse.\n", (int)ac->stream->codec->time_base.num,
+ (int)ac->stream->codec->time_base.den, (int)ac->stream->time_base.num,
+ (int)ac->stream->time_base.den);
+ ac->worst_time_base = ac->stream->codec->time_base;
+ ac->worst_time_base_is_stream = 0;
+ } else {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: NOTE: not using codec time "
+ "base (%d/%d) for pts adjustment; the stream base (%d/%d) "
+ "is worse.\n", (int)ac->stream->codec->time_base.num,
+ (int)ac->stream->codec->time_base.den, (int)ac->stream->time_base.num,
+ (int)ac->stream->time_base.den);
+ ac->worst_time_base = ac->stream->time_base;
+ ac->worst_time_base_is_stream = 1;
+ }
+
+ // NOTE: we use the following "axiom" of av_rescale_q:
+ // if time base A is worse than time base B, then
+ // av_rescale_q(av_rescale_q(x, A, B), B, A) == x
+ // this can be proven as long as av_rescale_q rounds to nearest, which
+ // it currently does
+
+ // av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B"
+ // and:
+ // av_rescale_q(av_rescale_q(x, A, B), B, A) * A
+ // == "round av_rescale_q(x, A, B)*B to nearest multiple of A"
+ // == "round 'round x*A to nearest multiple of B' to nearest multiple of A"
+ //
+ // assume this fails. Then there is a value of x*A, for which the
+ // nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[.
+ // Absurd, as this range MUST contain at least one multiple of B.
+ }
+
+ ptsoffset = ac->offset;
+ // this basically just edits ao->apts for syncing purposes
+
+ if (ectx->options->copyts || ectx->options->rawts) {
+ // we do not send time sync data to the video side,
+ // but we always need the exact pts, even if zero
+ } else {
+ // here we must "simulate" the pts editing
+ // 1. if we have to skip stuff, we skip it
+ // 2. if we have to add samples, we add them
+ // 3. we must still adjust ptsoffset appropriately for AV sync!
+ // invariant:
+ // if no partial skipping is done, the first frame gets ao->apts passed as pts!
+
+ if (ac->offset_left < 0) {
+ if (ac->offset_left <= -len) {
+ // skip whole frame
+ ac->offset_left += len;
+ return len * ac->sample_size * ao->channels;
+ } else {
+ // skip part of this frame, buffer/encode the rest
+ bufpos -= ac->offset_left;
+ ptsoffset += ac->offset_left;
+ ac->offset_left = 0;
+ }
+ } else if (ac->offset_left > 0) {
+ // make a temporary buffer, filled with zeroes at the start
+ // (don't worry, only happens once)
+
+ paddingbuf = talloc_size(ac, ac->sample_size * ao->channels *
+ (ac->offset_left + len));
+ fill_with_padding(paddingbuf, ac->offset_left, ac->sample_size,
+ ac->sample_padding);
+ data = (char *) paddingbuf + ac->sample_size * ao->channels *
+ ac->offset_left;
+ bufpos -= ac->offset_left; // yes, negative!
+ ptsoffset += ac->offset_left;
+ ac->offset_left = 0;
+
+ // now adjust the bufpos so the final value of bufpos is positive!
+ /*
+ int cnt = (len - bufpos) / ac->aframesize;
+ int finalbufpos = bufpos + cnt * ac->aframesize;
+ */
+ int finalbufpos = len - (len - bufpos) % ac->aframesize;
+ if (finalbufpos < 0) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "ao-lavc: cannot attain the "
+ "exact requested audio sync; shifting by %d frames\n",
+ -finalbufpos);
+ bufpos -= finalbufpos;
+ }
+ }
+ }
+
+ if (!ectx->options->rawts && ectx->options->copyts) {
+ // fix the discontinuity pts offset
+ nextpts = pts + ptsoffset / (double) ao->samplerate;
+ if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
+ ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
+ }
+ else if (fabs(nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts) > 30) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "ao-lavc: detected an unexpected discontinuity (pts jumped by "
+ "%f seconds)\n",
+ nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
+ ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
+ }
+
+ outpts = pts + ectx->discontinuity_pts_offset;
+ }
+ else
+ outpts = pts;
+
+ while (len - bufpos >= ac->aframesize) {
+ encode(ao,
+ outpts + (bufpos + ptsoffset) / (double) ao->samplerate + encode_lavc_getoffset(ectx, ac->stream),
+ (char *) data + ac->sample_size * bufpos * ao->channels);
+ bufpos += ac->aframesize;
+ }
+
+ talloc_free(paddingbuf);
+
+ // calculate expected pts of next audio frame
+ ac->expected_next_pts = pts + (bufpos + ptsoffset) / (double) ao->samplerate;
+
+ if (!ectx->options->rawts && ectx->options->copyts) {
+ // set next allowed output pts value
+ nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset;
+ if (nextpts > ectx->next_in_pts)
+ ectx->next_in_pts = nextpts;
+ }
+
+ return bufpos * ac->sample_size * ao->channels;
+}
+
+const struct ao_driver audio_out_lavc = {
+ .is_new = true,
+ .info = &(const struct ao_info) {
+ "audio encoding using libavcodec",
+ "lavc",
+ "Rudolf Polzer <divVerent@xonotic.org>",
+ ""
+ },
+ .init = init,
+ .uninit = uninit,
+ .get_space = get_space,
+ .play = play,
+};
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
new file mode 100644
index 0000000000..87f11a51b6
--- /dev/null
+++ b/audio/out/ao_null.c
@@ -0,0 +1,129 @@
+/*
+ * null audio output driver
+ *
+ * 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 "talloc.h"
+
+#include "config.h"
+#include "osdep/timer.h"
+#include "libaf/format.h"
+#include "audio_out.h"
+
+struct priv {
+ unsigned last_time;
+ float buffered_bytes;
+};
+
+static void drain(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ unsigned now = GetTimer();
+ priv->buffered_bytes -= (now - priv->last_time) / 1e6 * ao->bps;
+ if (priv->buffered_bytes < 0)
+ priv->buffered_bytes = 0;
+ priv->last_time = now;
+}
+
+static int init(struct ao *ao, char *params)
+{
+ struct priv *priv = talloc_zero(ao, struct priv);
+ ao->priv = priv;
+ int samplesize = af_fmt2bits(ao->format) / 8;
+ ao->outburst = 256 * ao->channels * samplesize;
+ // A "buffer" for about 0.2 seconds of audio
+ ao->buffersize = (int)(ao->samplerate * 0.2 / 256 + 1) * ao->outburst;
+ ao->bps = ao->channels * ao->samplerate * samplesize;
+ priv->last_time = GetTimer();
+
+ return 0;
+}
+
+// close audio device
+static void uninit(struct ao *ao, bool cut_audio)
+{
+}
+
+// stop playing and empty buffers (for seeking/pause)
+static void reset(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ priv->buffered_bytes = 0;
+}
+
+// stop playing, keep buffers (for pause)
+static void pause(struct ao *ao)
+{
+ // for now, just call reset();
+ reset(ao);
+}
+
+// resume playing, after audio_pause()
+static void resume(struct ao *ao)
+{
+}
+
+static int get_space(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ drain(ao);
+ return ao->buffersize - priv->buffered_bytes;
+}
+
+static int play(struct ao *ao, void *data, int len, int flags)
+{
+ struct priv *priv = ao->priv;
+
+ int maxbursts = (ao->buffersize - priv->buffered_bytes) / ao->outburst;
+ int playbursts = len / ao->outburst;
+ int bursts = playbursts > maxbursts ? maxbursts : playbursts;
+ priv->buffered_bytes += bursts * ao->outburst;
+ return bursts * ao->outburst;
+}
+
+static float get_delay(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ drain(ao);
+ return priv->buffered_bytes / ao->bps;
+}
+
+const struct ao_driver audio_out_null = {
+ .is_new = true,
+ .info = &(const struct ao_info) {
+ "Null audio output",
+ "null",
+ "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
+ "",
+ },
+ .init = init,
+ .uninit = uninit,
+ .reset = reset,
+ .get_space = get_space,
+ .play = play,
+ .get_delay = get_delay,
+ .pause = pause,
+ .resume = resume,
+};
+
diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c
new file mode 100644
index 0000000000..e5a40a769d
--- /dev/null
+++ b/audio/out/ao_openal.c
@@ -0,0 +1,280 @@
+/*
+ * OpenAL audio output driver for MPlayer
+ *
+ * Copyleft 2006 by Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.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
+ * along with MPlayer; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#ifdef OPENAL_AL_H
+#include <OpenAL/alc.h>
+#include <OpenAL/al.h>
+#include <OpenAL/alext.h>
+#else
+#include <AL/alc.h>
+#include <AL/al.h>
+#include <AL/alext.h>
+#endif
+
+#include "mp_msg.h"
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "libaf/format.h"
+#include "osdep/timer.h"
+#include "subopt-helper.h"
+
+static const ao_info_t info =
+{
+ "OpenAL audio output",
+ "openal",
+ "Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>",
+ ""
+};
+
+LIBAO_EXTERN(openal)
+
+#define MAX_CHANS 8
+#define NUM_BUF 128
+#define CHUNK_SIZE 512
+static ALuint buffers[MAX_CHANS][NUM_BUF];
+static ALuint sources[MAX_CHANS];
+
+static int cur_buf[MAX_CHANS];
+static int unqueue_buf[MAX_CHANS];
+static int16_t *tmpbuf;
+
+
+static int control(int cmd, void *arg) {
+ switch (cmd) {
+ case AOCONTROL_GET_VOLUME:
+ case AOCONTROL_SET_VOLUME: {
+ ALfloat volume;
+ ao_control_vol_t *vol = (ao_control_vol_t *)arg;
+ if (cmd == AOCONTROL_SET_VOLUME) {
+ volume = (vol->left + vol->right) / 200.0;
+ alListenerf(AL_GAIN, volume);
+ }
+ alGetListenerf(AL_GAIN, &volume);
+ vol->left = vol->right = volume * 100;
+ return CONTROL_TRUE;
+ }
+ }
+ return CONTROL_UNKNOWN;
+}
+
+/**
+ * \brief print suboption usage help
+ */
+static void print_help(void) {
+ mp_msg(MSGT_AO, MSGL_FATAL,
+ "\n-ao openal commandline help:\n"
+ "Example: mpv -ao openal:device=subdevice\n"
+ "\nOptions:\n"
+ " device=subdevice\n"
+ " Audio device OpenAL should use. Devices can be listed\n"
+ " with -ao openal:device=help\n"
+ );
+}
+
+static void list_devices(void) {
+ if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_TRUE) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "Device listing not supported.\n");
+ return;
+ }
+ const char *list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
+ mp_msg(MSGT_AO, MSGL_FATAL, "OpenAL devices:\n");
+ while (list && *list) {
+ mp_msg(MSGT_AO, MSGL_FATAL, " '%s'\n", list);
+ list = list + strlen(list) + 1;
+ }
+}
+
+static int init(int rate, int channels, int format, int flags) {
+ float position[3] = {0, 0, 0};
+ float direction[6] = {0, 0, 1, 0, -1, 0};
+ float sppos[MAX_CHANS][3] = {
+ {-1, 0, 0.5}, {1, 0, 0.5},
+ {-1, 0, -1}, {1, 0, -1},
+ {0, 0, 1}, {0, 0, 0.1},
+ {-1, 0, 0}, {1, 0, 0},
+ };
+ ALCdevice *dev = NULL;
+ ALCcontext *ctx = NULL;
+ ALCint freq = 0;
+ ALCint attribs[] = {ALC_FREQUENCY, rate, 0, 0};
+ int i;
+ char *device = NULL;
+ const opt_t subopts[] = {
+ {"device", OPT_ARG_MSTRZ, &device, NULL},
+ {NULL}
+ };
+ global_ao->no_persistent_volume = true;
+ if (subopt_parse(ao_subdevice, subopts) != 0) {
+ print_help();
+ return 0;
+ }
+ if (device && strcmp(device, "help") == 0) {
+ list_devices();
+ goto err_out;
+ }
+ if (channels > MAX_CHANS) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Invalid number of channels: %i\n", channels);
+ goto err_out;
+ }
+ dev = alcOpenDevice(device);
+ if (!dev) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] could not open device\n");
+ goto err_out;
+ }
+ ctx = alcCreateContext(dev, attribs);
+ alcMakeContextCurrent(ctx);
+ alListenerfv(AL_POSITION, position);
+ alListenerfv(AL_ORIENTATION, direction);
+ alGenSources(channels, sources);
+ for (i = 0; i < channels; i++) {
+ cur_buf[i] = 0;
+ unqueue_buf[i] = 0;
+ alGenBuffers(NUM_BUF, buffers[i]);
+ alSourcefv(sources[i], AL_POSITION, sppos[i]);
+ alSource3f(sources[i], AL_VELOCITY, 0, 0, 0);
+ }
+ if (channels == 1)
+ alSource3f(sources[0], AL_POSITION, 0, 0, 1);
+ ao_data.channels = channels;
+ alcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
+ if (alcGetError(dev) == ALC_NO_ERROR && freq)
+ rate = freq;
+ ao_data.samplerate = rate;
+ ao_data.format = AF_FORMAT_S16_NE;
+ ao_data.bps = channels * rate * 2;
+ ao_data.buffersize = CHUNK_SIZE * NUM_BUF;
+ ao_data.outburst = channels * CHUNK_SIZE;
+ tmpbuf = malloc(CHUNK_SIZE);
+ free(device);
+ return 1;
+
+err_out:
+ free(device);
+ return 0;
+}
+
+// close audio device
+static void uninit(int immed) {
+ ALCcontext *ctx = alcGetCurrentContext();
+ ALCdevice *dev = alcGetContextsDevice(ctx);
+ free(tmpbuf);
+ if (!immed) {
+ ALint state;
+ alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
+ while (state == AL_PLAYING) {
+ usec_sleep(10000);
+ alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
+ }
+ }
+ reset();
+ alcMakeContextCurrent(NULL);
+ alcDestroyContext(ctx);
+ alcCloseDevice(dev);
+}
+
+static void unqueue_buffers(void) {
+ ALint p;
+ int s;
+ for (s = 0; s < ao_data.channels; s++) {
+ int till_wrap = NUM_BUF - unqueue_buf[s];
+ alGetSourcei(sources[s], AL_BUFFERS_PROCESSED, &p);
+ if (p >= till_wrap) {
+ alSourceUnqueueBuffers(sources[s], till_wrap, &buffers[s][unqueue_buf[s]]);
+ unqueue_buf[s] = 0;
+ p -= till_wrap;
+ }
+ if (p) {
+ alSourceUnqueueBuffers(sources[s], p, &buffers[s][unqueue_buf[s]]);
+ unqueue_buf[s] += p;
+ }
+ }
+}
+
+/**
+ * \brief stop playing and empty buffers (for seeking/pause)
+ */
+static void reset(void) {
+ alSourceStopv(ao_data.channels, sources);
+ unqueue_buffers();
+}
+
+/**
+ * \brief stop playing, keep buffers (for pause)
+ */
+static void audio_pause(void) {
+ alSourcePausev(ao_data.channels, sources);
+}
+
+/**
+ * \brief resume playing, after audio_pause()
+ */
+static void audio_resume(void) {
+ alSourcePlayv(ao_data.channels, sources);
+}
+
+static int get_space(void) {
+ ALint queued;
+ unqueue_buffers();
+ alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued);
+ queued = NUM_BUF - queued - 3;
+ if (queued < 0) return 0;
+ return queued * CHUNK_SIZE * ao_data.channels;
+}
+
+/**
+ * \brief write data into buffer and reset underrun flag
+ */
+static int play(void *data, int len, int flags) {
+ ALint state;
+ int i, j, k;
+ int ch;
+ int16_t *d = data;
+ len /= ao_data.channels * CHUNK_SIZE;
+ for (i = 0; i < len; i++) {
+ for (ch = 0; ch < ao_data.channels; ch++) {
+ for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels)
+ tmpbuf[j] = d[k];
+ alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, tmpbuf,
+ CHUNK_SIZE, ao_data.samplerate);
+ alSourceQueueBuffers(sources[ch], 1, &buffers[ch][cur_buf[ch]]);
+ cur_buf[ch] = (cur_buf[ch] + 1) % NUM_BUF;
+ }
+ d += ao_data.channels * CHUNK_SIZE / 2;
+ }
+ alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
+ if (state != AL_PLAYING) // checked here in case of an underrun
+ alSourcePlayv(ao_data.channels, sources);
+ return len * ao_data.channels * CHUNK_SIZE;
+}
+
+static float get_delay(void) {
+ ALint queued;
+ unqueue_buffers();
+ alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued);
+ return queued * CHUNK_SIZE / 2 / (float)ao_data.samplerate;
+}
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c
new file mode 100644
index 0000000000..9d4dde4837
--- /dev/null
+++ b/audio/out/ao_oss.c
@@ -0,0 +1,560 @@
+/*
+ * OSS audio output driver
+ *
+ * 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 <sys/ioctl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "mixer.h"
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h>
+#else
+#ifdef HAVE_SOUNDCARD_H
+#include <soundcard.h>
+#endif
+#endif
+
+#include "libaf/format.h"
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+
+static const ao_info_t info =
+{
+ "OSS/ioctl audio output",
+ "oss",
+ "A'rpi",
+ ""
+};
+
+/* Support for >2 output channels added 2001-11-25 - Steve Davies <steve@daviesfam.org> */
+
+LIBAO_EXTERN(oss)
+
+static int format2oss(int format)
+{
+ switch(format)
+ {
+ case AF_FORMAT_U8: return AFMT_U8;
+ case AF_FORMAT_S8: return AFMT_S8;
+ case AF_FORMAT_U16_LE: return AFMT_U16_LE;
+ case AF_FORMAT_U16_BE: return AFMT_U16_BE;
+ case AF_FORMAT_S16_LE: return AFMT_S16_LE;
+ case AF_FORMAT_S16_BE: return AFMT_S16_BE;
+#ifdef AFMT_S24_PACKED
+ case AF_FORMAT_S24_LE: return AFMT_S24_PACKED;
+#endif
+#ifdef AFMT_U32_LE
+ case AF_FORMAT_U32_LE: return AFMT_U32_LE;
+#endif
+#ifdef AFMT_U32_BE
+ case AF_FORMAT_U32_BE: return AFMT_U32_BE;
+#endif
+#ifdef AFMT_S32_LE
+ case AF_FORMAT_S32_LE: return AFMT_S32_LE;
+#endif
+#ifdef AFMT_S32_BE
+ case AF_FORMAT_S32_BE: return AFMT_S32_BE;
+#endif
+#ifdef AFMT_FLOAT
+ case AF_FORMAT_FLOAT_NE: return AFMT_FLOAT;
+#endif
+ // SPECIALS
+ case AF_FORMAT_MU_LAW: return AFMT_MU_LAW;
+ case AF_FORMAT_A_LAW: return AFMT_A_LAW;
+ case AF_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;
+#ifdef AFMT_MPEG
+ case AF_FORMAT_MPEG2: return AFMT_MPEG;
+#endif
+#ifdef AFMT_AC3
+ case AF_FORMAT_AC3_NE: return AFMT_AC3;
+#endif
+ }
+ mp_msg(MSGT_AO, MSGL_V, "OSS: Unknown/not supported internal format: %s\n", af_fmt2str_short(format));
+ return -1;
+}
+
+static int oss2format(int format)
+{
+ switch(format)
+ {
+ case AFMT_U8: return AF_FORMAT_U8;
+ case AFMT_S8: return AF_FORMAT_S8;
+ case AFMT_U16_LE: return AF_FORMAT_U16_LE;
+ case AFMT_U16_BE: return AF_FORMAT_U16_BE;
+ case AFMT_S16_LE: return AF_FORMAT_S16_LE;
+ case AFMT_S16_BE: return AF_FORMAT_S16_BE;
+#ifdef AFMT_S24_PACKED
+ case AFMT_S24_PACKED: return AF_FORMAT_S24_LE;
+#endif
+#ifdef AFMT_U32_LE
+ case AFMT_U32_LE: return AF_FORMAT_U32_LE;
+#endif
+#ifdef AFMT_U32_BE
+ case AFMT_U32_BE: return AF_FORMAT_U32_BE;
+#endif
+#ifdef AFMT_S32_LE
+ case AFMT_S32_LE: return AF_FORMAT_S32_LE;
+#endif
+#ifdef AFMT_S32_BE
+ case AFMT_S32_BE: return AF_FORMAT_S32_BE;
+#endif
+#ifdef AFMT_FLOAT
+ case AFMT_FLOAT: return AF_FORMAT_FLOAT_NE;
+#endif
+ // SPECIALS
+ case AFMT_MU_LAW: return AF_FORMAT_MU_LAW;
+ case AFMT_A_LAW: return AF_FORMAT_A_LAW;
+ case AFMT_IMA_ADPCM: return AF_FORMAT_IMA_ADPCM;
+#ifdef AFMT_MPEG
+ case AFMT_MPEG: return AF_FORMAT_MPEG2;
+#endif
+#ifdef AFMT_AC3
+ case AFMT_AC3: return AF_FORMAT_AC3_NE;
+#endif
+ }
+ mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[AO OSS] Unknown/Unsupported OSS format: %x.\n", format);
+ return -1;
+}
+
+static char *dsp=PATH_DEV_DSP;
+static audio_buf_info zz;
+static int audio_fd=-1;
+static int prepause_space;
+
+static const char *oss_mixer_device = PATH_DEV_MIXER;
+static int oss_mixer_channel = SOUND_MIXER_PCM;
+
+#ifdef SNDCTL_DSP_GETPLAYVOL
+static int volume_oss4(ao_control_vol_t *vol, int cmd) {
+ int v;
+
+ if (audio_fd < 0)
+ return CONTROL_ERROR;
+
+ if (cmd == AOCONTROL_GET_VOLUME) {
+ if (ioctl(audio_fd, SNDCTL_DSP_GETPLAYVOL, &v) == -1)
+ return CONTROL_ERROR;
+ vol->right = (v & 0xff00) >> 8;
+ vol->left = v & 0x00ff;
+ return CONTROL_OK;
+ } else if (cmd == AOCONTROL_SET_VOLUME) {
+ v = ((int) vol->right << 8) | (int) vol->left;
+ if (ioctl(audio_fd, SNDCTL_DSP_SETPLAYVOL, &v) == -1)
+ return CONTROL_ERROR;
+ return CONTROL_OK;
+ } else
+ return CONTROL_UNKNOWN;
+}
+#endif
+
+// to set/get/query special features/parameters
+static int control(int cmd,void *arg){
+ switch(cmd){
+ case AOCONTROL_GET_VOLUME:
+ case AOCONTROL_SET_VOLUME:
+ {
+ ao_control_vol_t *vol = (ao_control_vol_t *)arg;
+ int fd, v, devs;
+
+#ifdef SNDCTL_DSP_GETPLAYVOL
+ // Try OSS4 first
+ if (volume_oss4(vol, cmd) == CONTROL_OK)
+ return CONTROL_OK;
+#endif
+
+ if(AF_FORMAT_IS_AC3(ao_data.format))
+ return CONTROL_TRUE;
+
+ if ((fd = open(oss_mixer_device, O_RDONLY)) != -1)
+ {
+ ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs);
+ if (devs & (1 << oss_mixer_channel))
+ {
+ if (cmd == AOCONTROL_GET_VOLUME)
+ {
+ ioctl(fd, MIXER_READ(oss_mixer_channel), &v);
+ vol->right = (v & 0xFF00) >> 8;
+ vol->left = v & 0x00FF;
+ }
+ else
+ {
+ v = ((int)vol->right << 8) | (int)vol->left;
+ ioctl(fd, MIXER_WRITE(oss_mixer_channel), &v);
+ }
+ }
+ else
+ {
+ close(fd);
+ return CONTROL_ERROR;
+ }
+ close(fd);
+ return CONTROL_OK;
+ }
+ }
+ return CONTROL_ERROR;
+ }
+ return CONTROL_UNKNOWN;
+}
+
+// open & setup audio device
+// return: 1=success 0=fail
+static int init(int rate,int channels,int format,int flags){
+ char *mixer_channels [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
+ int oss_format;
+ char *mdev = mixer_device, *mchan = mixer_channel;
+
+ mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz %d chans %s\n",rate,channels,
+ af_fmt2str_short(format));
+
+ if (ao_subdevice) {
+ char *m,*c;
+ m = strchr(ao_subdevice,':');
+ if(m) {
+ c = strchr(m+1,':');
+ if(c) {
+ mchan = c+1;
+ c[0] = '\0';
+ }
+ mdev = m+1;
+ m[0] = '\0';
+ }
+ dsp = ao_subdevice;
+ }
+
+ if(mdev)
+ oss_mixer_device=mdev;
+ else
+ oss_mixer_device=PATH_DEV_MIXER;
+
+ if(mchan){
+ int fd, devs, i;
+
+ if ((fd = open(oss_mixer_device, O_RDONLY)) == -1){
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Can't open mixer device %s: %s\n",
+ oss_mixer_device, strerror(errno));
+ }else{
+ ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs);
+ close(fd);
+
+ for (i=0; i<SOUND_MIXER_NRDEVICES; i++){
+ if(!strcasecmp(mixer_channels[i], mchan)){
+ if(!(devs & (1 << i))){
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Audio card mixer does not have channel '%s', using default.\n",mchan);
+ i = SOUND_MIXER_NRDEVICES+1;
+ break;
+ }
+ oss_mixer_channel = i;
+ break;
+ }
+ }
+ if(i==SOUND_MIXER_NRDEVICES){
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Audio card mixer does not have channel '%s', using default.\n",mchan);
+ }
+ }
+ } else
+ oss_mixer_channel = SOUND_MIXER_PCM;
+
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: using '%s' dsp device\n", dsp);
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: using '%s' mixer device\n", oss_mixer_device);
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: using '%s' mixer device\n", mixer_channels[oss_mixer_channel]);
+
+#ifdef __linux__
+ audio_fd=open(dsp, O_WRONLY | O_NONBLOCK);
+#else
+ audio_fd=open(dsp, O_WRONLY);
+#endif
+ if(audio_fd<0){
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Can't open audio device %s: %s\n", dsp, strerror(errno));
+ return 0;
+ }
+
+#ifdef __linux__
+ /* Remove the non-blocking flag */
+ if(fcntl(audio_fd, F_SETFL, 0) < 0) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Can't make file descriptor blocking: %s\n", strerror(errno));
+ return 0;
+ }
+#endif
+
+#if defined(FD_CLOEXEC) && defined(F_SETFD)
+ fcntl(audio_fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+ if(AF_FORMAT_IS_AC3(format)) {
+ ao_data.samplerate=rate;
+ ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
+ }
+
+ac3_retry:
+ if (AF_FORMAT_IS_AC3(format))
+ format = AF_FORMAT_AC3_NE;
+ ao_data.format=format;
+ oss_format=format2oss(format);
+ if (oss_format == -1) {
+#if BYTE_ORDER == BIG_ENDIAN
+ oss_format=AFMT_S16_BE;
+#else
+ oss_format=AFMT_S16_LE;
+#endif
+ format=AF_FORMAT_S16_NE;
+ }
+ if( ioctl(audio_fd, SNDCTL_DSP_SETFMT, &oss_format)<0 ||
+ oss_format != format2oss(format)) {
+ mp_tmsg(MSGT_AO,MSGL_WARN, "[AO OSS] Can't set audio device %s to %s output, trying %s...\n", dsp,
+ af_fmt2str_short(format), af_fmt2str_short(AF_FORMAT_S16_NE) );
+ format=AF_FORMAT_S16_NE;
+ goto ac3_retry;
+ }
+#if 0
+ if(oss_format!=format2oss(format))
+ mp_msg(MSGT_AO,MSGL_WARN,"WARNING! Your soundcard does NOT support %s sample format! Broken audio or bad playback speed are possible! Try with '-af format'\n",audio_out_format_name(format));
+#endif
+
+ ao_data.format = oss2format(oss_format);
+ if (ao_data.format == -1) return 0;
+
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: sample format: %s (requested: %s)\n",
+ af_fmt2str_short(ao_data.format), af_fmt2str_short(format));
+
+ ao_data.channels = channels;
+ if(!AF_FORMAT_IS_AC3(format)) {
+ // We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
+ if (ao_data.channels > 2) {
+ if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels) == -1 ||
+ ao_data.channels != channels ) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", channels);
+ return 0;
+ }
+ }
+ else {
+ int c = ao_data.channels-1;
+ if (ioctl (audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", ao_data.channels);
+ return 0;
+ }
+ ao_data.channels=c+1;
+ }
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels, channels);
+ // set rate
+ ao_data.samplerate=rate;
+ ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d Hz samplerate (requested: %d)\n",ao_data.samplerate,rate);
+ }
+
+ if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)==-1){
+ int r=0;
+ mp_tmsg(MSGT_AO,MSGL_WARN,"[AO OSS] audio_setup: driver doesn't support SNDCTL_DSP_GETOSPACE :-(\n");
+ if(ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &r)==-1){
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: %d bytes/frag (config.h)\n",ao_data.outburst);
+ } else {
+ ao_data.outburst=r;
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: %d bytes/frag (GETBLKSIZE)\n",ao_data.outburst);
+ }
+ } else {
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: frags: %3d/%d (%d bytes/frag) free: %6d\n",
+ zz.fragments, zz.fragstotal, zz.fragsize, zz.bytes);
+ if(ao_data.buffersize==-1) ao_data.buffersize=zz.bytes;
+ ao_data.outburst=zz.fragsize;
+ }
+
+ if(ao_data.buffersize==-1){
+ // Measuring buffer size:
+ void* data;
+ ao_data.buffersize=0;
+#ifdef HAVE_AUDIO_SELECT
+ data=malloc(ao_data.outburst); memset(data,0,ao_data.outburst);
+ while(ao_data.buffersize<0x40000){
+ fd_set rfds;
+ struct timeval tv;
+ FD_ZERO(&rfds); FD_SET(audio_fd,&rfds);
+ tv.tv_sec=0; tv.tv_usec = 0;
+ if(!select(audio_fd+1, NULL, &rfds, NULL, &tv)) break;
+ write(audio_fd,data,ao_data.outburst);
+ ao_data.buffersize+=ao_data.outburst;
+ }
+ free(data);
+ if(ao_data.buffersize==0){
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS]\n *** Your audio driver DOES NOT support select() ***\n Recompile mpv with #undef HAVE_AUDIO_SELECT in config.h !\n\n");
+ return 0;
+ }
+#endif
+ }
+
+ ao_data.bps=ao_data.channels;
+ switch (ao_data.format & AF_FORMAT_BITS_MASK) {
+ case AF_FORMAT_8BIT:
+ break;
+ case AF_FORMAT_16BIT:
+ ao_data.bps*=2;
+ break;
+ case AF_FORMAT_24BIT:
+ ao_data.bps*=3;
+ break;
+ case AF_FORMAT_32BIT:
+ ao_data.bps*=4;
+ break;
+ }
+
+ ao_data.outburst-=ao_data.outburst % ao_data.bps; // round down
+ ao_data.bps*=ao_data.samplerate;
+
+ return 1;
+}
+
+// close audio device
+static void uninit(int immed){
+ if(audio_fd == -1) return;
+#ifdef SNDCTL_DSP_SYNC
+ // to get the buffer played
+ if (!immed)
+ ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL);
+#endif
+#ifdef SNDCTL_DSP_RESET
+ if (immed)
+ ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
+#endif
+ close(audio_fd);
+ audio_fd = -1;
+}
+
+// stop playing and empty buffers (for seeking/pause)
+static void reset(void){
+ int oss_format;
+ uninit(1);
+ audio_fd=open(dsp, O_WRONLY);
+ if(audio_fd < 0){
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS]\nFatal error: *** CANNOT RE-OPEN / RESET AUDIO DEVICE *** %s\n", strerror(errno));
+ return;
+ }
+
+#if defined(FD_CLOEXEC) && defined(F_SETFD)
+ fcntl(audio_fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+ oss_format = format2oss(ao_data.format);
+ if(AF_FORMAT_IS_AC3(ao_data.format))
+ ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
+ ioctl (audio_fd, SNDCTL_DSP_SETFMT, &oss_format);
+ if(!AF_FORMAT_IS_AC3(ao_data.format)) {
+ if (ao_data.channels > 2)
+ ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels);
+ else {
+ int c = ao_data.channels-1;
+ ioctl (audio_fd, SNDCTL_DSP_STEREO, &c);
+ }
+ ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
+ }
+}
+
+// stop playing, keep buffers (for pause)
+static void audio_pause(void)
+{
+ prepause_space = get_space();
+ uninit(1);
+}
+
+// resume playing, after audio_pause()
+static void audio_resume(void)
+{
+ int fillcnt;
+ reset();
+ fillcnt = get_space() - prepause_space;
+ if (fillcnt > 0 && !(ao_data.format & AF_FORMAT_SPECIAL_MASK)) {
+ void *silence = calloc(fillcnt, 1);
+ play(silence, fillcnt, 0);
+ free(silence);
+ }
+}
+
+
+// return: how many bytes can be played without blocking
+static int get_space(void){
+ int playsize=ao_data.outburst;
+
+#ifdef SNDCTL_DSP_GETOSPACE
+ if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)!=-1){
+ // calculate exact buffer space:
+ playsize = zz.fragments*zz.fragsize;
+ return playsize;
+ }
+#endif
+
+ // check buffer
+#ifdef HAVE_AUDIO_SELECT
+ { fd_set rfds;
+ struct timeval tv;
+ FD_ZERO(&rfds);
+ FD_SET(audio_fd, &rfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ if(!select(audio_fd+1, NULL, &rfds, NULL, &tv)) return 0; // not block!
+ }
+#endif
+
+ return ao_data.outburst;
+}
+
+// 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){
+ if(len==0)
+ return len;
+ if(len>ao_data.outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
+ len/=ao_data.outburst;
+ len*=ao_data.outburst;
+ }
+ len=write(audio_fd,data,len);
+ return len;
+}
+
+static int audio_delay_method=2;
+
+// return: delay in seconds between first and last sample in buffer
+static float get_delay(void){
+ /* Calculate how many bytes/second is sent out */
+ if(audio_delay_method==2){
+#ifdef SNDCTL_DSP_GETODELAY
+ int r=0;
+ if(ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &r)!=-1)
+ return ((float)r)/(float)ao_data.bps;
+#endif
+ audio_delay_method=1; // fallback if not supported
+ }
+ if(audio_delay_method==1){
+ // SNDCTL_DSP_GETOSPACE
+ if(ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz)!=-1)
+ return ((float)(ao_data.buffersize-zz.bytes))/(float)ao_data.bps;
+ audio_delay_method=0; // fallback if not supported
+ }
+ return ((float)ao_data.buffersize)/(float)ao_data.bps;
+}
diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c
new file mode 100644
index 0000000000..0b1c527e89
--- /dev/null
+++ b/audio/out/ao_pcm.c
@@ -0,0 +1,256 @@
+/*
+ * PCM audio output driver
+ *
+ * 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 "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libavutil/common.h>
+
+#include "talloc.h"
+
+#include "subopt-helper.h"
+#include "libaf/format.h"
+#include "libaf/reorder_ch.h"
+#include "audio_out.h"
+#include "mp_msg.h"
+
+#ifdef __MINGW32__
+// for GetFileType to detect pipes
+#include <windows.h>
+#include <io.h>
+#endif
+
+struct priv {
+ char *outputfilename;
+ int waveheader;
+ uint64_t data_length;
+ FILE *fp;
+};
+
+#define WAV_ID_RIFF 0x46464952 /* "RIFF" */
+#define WAV_ID_WAVE 0x45564157 /* "WAVE" */
+#define WAV_ID_FMT 0x20746d66 /* "fmt " */
+#define WAV_ID_DATA 0x61746164 /* "data" */
+#define WAV_ID_PCM 0x0001
+#define WAV_ID_FLOAT_PCM 0x0003
+#define WAV_ID_FORMAT_EXTENSIBLE 0xfffe
+
+static void fput16le(uint16_t val, FILE *fp)
+{
+ uint8_t bytes[2] = {val, val >> 8};
+ fwrite(bytes, 1, 2, fp);
+}
+
+static void fput32le(uint32_t val, FILE *fp)
+{
+ uint8_t bytes[4] = {val, val >> 8, val >> 16, val >> 24};
+ fwrite(bytes, 1, 4, fp);
+}
+
+static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
+{
+ bool use_waveex = ao->channels >= 5 && ao->channels <= 8;
+ uint16_t fmt = ao->format == AF_FORMAT_FLOAT_LE ?
+ WAV_ID_FLOAT_PCM : WAV_ID_PCM;
+ uint32_t fmt_chunk_size = use_waveex ? 40 : 16;
+ int bits = af_fmt2bits(ao->format);
+
+ // Master RIFF chunk
+ fput32le(WAV_ID_RIFF, fp);
+ // RIFF chunk size: 'WAVE' + 'fmt ' + 4 + fmt_chunk_size +
+ // data chunk hdr (8) + data length
+ fput32le(12 + fmt_chunk_size + 8 + data_length, fp);
+ fput32le(WAV_ID_WAVE, fp);
+
+ // Format chunk
+ fput32le(WAV_ID_FMT, fp);
+ fput32le(fmt_chunk_size, fp);
+ fput16le(use_waveex ? WAV_ID_FORMAT_EXTENSIBLE : fmt, fp);
+ fput16le(ao->channels, fp);
+ fput32le(ao->samplerate, fp);
+ fput32le(ao->bps, fp);
+ fput16le(ao->channels * (bits / 8), fp);
+ fput16le(bits, fp);
+
+ if (use_waveex) {
+ // Extension chunk
+ fput16le(22, fp);
+ fput16le(bits, fp);
+ switch (ao->channels) {
+ case 5:
+ fput32le(0x0607, fp); // L R C Lb Rb
+ break;
+ case 6:
+ fput32le(0x060f, fp); // L R C Lb Rb LFE
+ break;
+ case 7:
+ fput32le(0x0727, fp); // L R C Cb Ls Rs LFE
+ break;
+ case 8:
+ fput32le(0x063f, fp); // L R C Lb Rb Ls Rs LFE
+ break;
+ }
+ // 2 bytes format + 14 bytes guid
+ fput32le(fmt, fp);
+ fput32le(0x00100000, fp);
+ fput32le(0xAA000080, fp);
+ fput32le(0x719B3800, fp);
+ }
+
+ // Data chunk
+ fput32le(WAV_ID_DATA, fp);
+ fput32le(data_length, fp);
+}
+
+static int init(struct ao *ao, char *params)
+{
+ struct priv *priv = talloc_zero(ao, struct priv);
+ ao->priv = priv;
+
+ int fast = 0;
+ const opt_t subopts[] = {
+ {"waveheader", OPT_ARG_BOOL, &priv->waveheader, NULL},
+ {"file", OPT_ARG_MSTRZ, &priv->outputfilename, NULL},
+ {"fast", OPT_ARG_BOOL, &fast, NULL},
+ {NULL}
+ };
+ // set defaults
+ priv->waveheader = 1;
+
+ if (subopt_parse(params, subopts) != 0)
+ return -1;
+
+ if (fast)
+ mp_msg(MSGT_AO, MSGL_WARN,
+ "[AO PCM] Suboption \"fast\" is deprecated.\n"
+ "[AO PCM] Use -novideo, or -benchmark if you want "
+ "faster playback with video.\n");
+ if (!priv->outputfilename)
+ priv->outputfilename =
+ strdup(priv->waveheader ? "audiodump.wav" : "audiodump.pcm");
+ if (priv->waveheader) {
+ // WAV files must have one of the following formats
+
+ switch (ao->format) {
+ case AF_FORMAT_U8:
+ case AF_FORMAT_S16_LE:
+ case AF_FORMAT_S24_LE:
+ case AF_FORMAT_S32_LE:
+ case AF_FORMAT_FLOAT_LE:
+ case AF_FORMAT_AC3_BE:
+ case AF_FORMAT_AC3_LE:
+ break;
+ default:
+ ao->format = AF_FORMAT_S16_LE;
+ break;
+ }
+ }
+
+ ao->outburst = 65536;
+ ao->bps = ao->channels * ao->samplerate * (af_fmt2bits(ao->format) / 8);
+
+ mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] File: %s (%s)\n"
+ "PCM: Samplerate: %d Hz Channels: %d Format: %s\n",
+ priv->outputfilename,
+ priv->waveheader ? "WAVE" : "RAW PCM", ao->samplerate,
+ ao->channels, af_fmt2str_short(ao->format));
+ mp_tmsg(MSGT_AO, MSGL_INFO,
+ "[AO PCM] Info: Faster dumping is achieved with -novideo\n"
+ "[AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).\n");
+
+ priv->fp = fopen(priv->outputfilename, "wb");
+ if (!priv->fp) {
+ mp_tmsg(MSGT_AO, MSGL_ERR, "[AO PCM] Failed to open %s for writing!\n",
+ priv->outputfilename);
+ return -1;
+ }
+ if (priv->waveheader) // Reserve space for wave header
+ write_wave_header(ao, priv->fp, 0x7ffff000);
+ ao->untimed = true;
+
+ return 0;
+}
+
+// close audio device
+static void uninit(struct ao *ao, bool cut_audio)
+{
+ struct priv *priv = ao->priv;
+
+ if (priv->waveheader) { // Rewrite wave header
+ bool broken_seek = false;
+#ifdef __MINGW32__
+ // Windows, in its usual idiocy "emulates" seeks on pipes so it always
+ // looks like they work. So we have to detect them brute-force.
+ broken_seek = FILE_TYPE_DISK !=
+ GetFileType((HANDLE)_get_osfhandle(_fileno(priv->fp)));
+#endif
+ if (broken_seek || fseek(priv->fp, 0, SEEK_SET) != 0)
+ mp_msg(MSGT_AO, MSGL_ERR, "Could not seek to start, "
+ "WAV size headers not updated!\n");
+ else {
+ if (priv->data_length > 0xfffff000) {
+ mp_msg(MSGT_AO, MSGL_ERR, "File larger than allowed for "
+ "WAV files, may play truncated!\n");
+ priv->data_length = 0xfffff000;
+ }
+ write_wave_header(ao, priv->fp, priv->data_length);
+ }
+ }
+ fclose(priv->fp);
+ free(priv->outputfilename);
+}
+
+static int get_space(struct ao *ao)
+{
+ return ao->outburst;
+}
+
+static int play(struct ao *ao, void *data, int len, int flags)
+{
+ struct priv *priv = ao->priv;
+
+ if (ao->channels == 5 || ao->channels == 6 || ao->channels == 8) {
+ int frame_size = af_fmt2bits(ao->format) / 8;
+ len -= len % (frame_size * ao->channels);
+ reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
+ AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
+ ao->channels, len / frame_size, frame_size);
+ }
+ fwrite(data, len, 1, priv->fp);
+ priv->data_length += len;
+ return len;
+}
+
+const struct ao_driver audio_out_pcm = {
+ .is_new = true,
+ .info = &(const struct ao_info) {
+ "RAW PCM/WAVE file writer audio output",
+ "pcm",
+ "Atmosfear",
+ "",
+ },
+ .init = init,
+ .uninit = uninit,
+ .get_space = get_space,
+ .play = play,
+};
diff --git a/audio/out/ao_portaudio.c b/audio/out/ao_portaudio.c
new file mode 100644
index 0000000000..36b08f8288
--- /dev/null
+++ b/audio/out/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/common.h>
+#include <portaudio.h>
+
+#include "config.h"
+#include "subopt-helper.h"
+#include "libaf/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: mpv -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/audio/out/ao_pulse.c b/audio/out/ao_pulse.c
new file mode 100644
index 0000000000..1d2ebc5281
--- /dev/null
+++ b/audio/out/ao_pulse.c
@@ -0,0 +1,554 @@
+/*
+ * PulseAudio audio output driver.
+ * Copyright (C) 2006 Lennart Poettering
+ * Copyright (C) 2007 Reimar Doeffinger
+ *
+ * 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 <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "config.h"
+#include "libaf/format.h"
+#include "mp_msg.h"
+#include "audio_out.h"
+#include "input/input.h"
+
+#define PULSE_CLIENT_NAME "mpv"
+
+#define VOL_PA2MP(v) ((v) * 100 / PA_VOLUME_UI_MAX)
+#define VOL_MP2PA(v) ((v) * PA_VOLUME_UI_MAX / 100)
+
+struct priv {
+ // PulseAudio playback stream object
+ struct pa_stream *stream;
+
+ // PulseAudio connection context
+ struct pa_context *context;
+
+ // Main event loop object
+ struct pa_threaded_mainloop *mainloop;
+
+ // temporary during control()
+ struct pa_sink_input_info pi;
+
+ bool broken_pause;
+ int retval;
+};
+
+#define GENERIC_ERR_MSG(ctx, str) \
+ mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] "str": %s\n", \
+ pa_strerror(pa_context_errno(ctx)))
+
+static void context_state_cb(pa_context *c, void *userdata)
+{
+ struct ao *ao = userdata;
+ struct priv *priv = ao->priv;
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(priv->mainloop, 0);
+ break;
+ }
+}
+
+static void stream_state_cb(pa_stream *s, void *userdata)
+{
+ struct ao *ao = userdata;
+ struct priv *priv = ao->priv;
+ switch (pa_stream_get_state(s)) {
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal(priv->mainloop, 0);
+ break;
+ }
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
+{
+ struct ao *ao = userdata;
+ struct priv *priv = ao->priv;
+ mp_input_wakeup(ao->input_ctx);
+ pa_threaded_mainloop_signal(priv->mainloop, 0);
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata)
+{
+ struct ao *ao = userdata;
+ struct priv *priv = ao->priv;
+ pa_threaded_mainloop_signal(priv->mainloop, 0);
+}
+
+static void success_cb(pa_stream *s, int success, void *userdata)
+{
+ struct ao *ao = userdata;
+ struct priv *priv = ao->priv;
+ priv->retval = success;
+ pa_threaded_mainloop_signal(priv->mainloop, 0);
+}
+
+/**
+ * \brief waits for a pulseaudio operation to finish, frees it and
+ * unlocks the mainloop
+ * \param op operation to wait for
+ * \return 1 if operation has finished normally (DONE state), 0 otherwise
+ */
+static int waitop(struct priv *priv, pa_operation *op)
+{
+ if (!op) {
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ return 0;
+ }
+ pa_operation_state_t state = pa_operation_get_state(op);
+ while (state == PA_OPERATION_RUNNING) {
+ pa_threaded_mainloop_wait(priv->mainloop);
+ state = pa_operation_get_state(op);
+ }
+ pa_operation_unref(op);
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ return state == PA_OPERATION_DONE;
+}
+
+static const struct format_map {
+ int mp_format;
+ pa_sample_format_t pa_format;
+} format_maps[] = {
+ {AF_FORMAT_S16_LE, PA_SAMPLE_S16LE},
+ {AF_FORMAT_S16_BE, PA_SAMPLE_S16BE},
+ {AF_FORMAT_S32_LE, PA_SAMPLE_S32LE},
+ {AF_FORMAT_S32_BE, PA_SAMPLE_S32BE},
+ {AF_FORMAT_FLOAT_LE, PA_SAMPLE_FLOAT32LE},
+ {AF_FORMAT_FLOAT_BE, PA_SAMPLE_FLOAT32BE},
+ {AF_FORMAT_U8, PA_SAMPLE_U8},
+ {AF_FORMAT_MU_LAW, PA_SAMPLE_ULAW},
+ {AF_FORMAT_A_LAW, PA_SAMPLE_ALAW},
+ {AF_FORMAT_UNKNOWN, 0}
+};
+
+static void uninit(struct ao *ao, bool cut_audio)
+{
+ struct priv *priv = ao->priv;
+ if (priv->stream && !cut_audio) {
+ pa_threaded_mainloop_lock(priv->mainloop);
+ waitop(priv, pa_stream_drain(priv->stream, success_cb, ao));
+ }
+
+ if (priv->mainloop)
+ pa_threaded_mainloop_stop(priv->mainloop);
+
+ if (priv->stream) {
+ pa_stream_disconnect(priv->stream);
+ pa_stream_unref(priv->stream);
+ priv->stream = NULL;
+ }
+
+ if (priv->context) {
+ pa_context_disconnect(priv->context);
+ pa_context_unref(priv->context);
+ priv->context = NULL;
+ }
+
+ if (priv->mainloop) {
+ pa_threaded_mainloop_free(priv->mainloop);
+ priv->mainloop = NULL;
+ }
+}
+
+static int init(struct ao *ao, char *params)
+{
+ struct pa_sample_spec ss;
+ struct pa_channel_map map;
+ char *devarg = NULL;
+ char *host = NULL;
+ char *sink = NULL;
+ const char *version = pa_get_library_version();
+
+ struct priv *priv = talloc_zero(ao, struct priv);
+ ao->priv = priv;
+
+ ao->per_application_mixer = true;
+
+ if (params) {
+ devarg = strdup(params);
+ sink = strchr(devarg, ':');
+ if (sink)
+ *sink++ = 0;
+ if (devarg[0])
+ host = devarg;
+ }
+
+ priv->broken_pause = false;
+ /* not sure which versions are affected, assume 0.9.11* to 0.9.14*
+ * known bad: 0.9.14, 0.9.13
+ * known good: 0.9.9, 0.9.10, 0.9.15
+ * To test: pause, wait ca. 5 seconds, framestep and see if MPlayer
+ * hangs somewhen. */
+ if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1'
+ && version[5] <= '4') {
+ mp_msg(MSGT_AO, MSGL_WARN,
+ "[pulse] working around probably broken pause functionality,\n"
+ " see http://www.pulseaudio.org/ticket/440\n");
+ priv->broken_pause = true;
+ }
+
+ ss.channels = ao->channels;
+ ss.rate = ao->samplerate;
+
+ const struct format_map *fmt_map = format_maps;
+ while (fmt_map->mp_format != ao->format) {
+ if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
+ mp_msg(MSGT_AO, MSGL_V,
+ "AO: [pulse] Unsupported format, using default\n");
+ fmt_map = format_maps;
+ break;
+ }
+ fmt_map++;
+ }
+ ao->format = fmt_map->mp_format;
+ ss.format = fmt_map->pa_format;
+
+ if (!pa_sample_spec_valid(&ss)) {
+ mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
+ goto fail;
+ }
+
+ pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
+ ao->bps = pa_bytes_per_second(&ss);
+
+ if (!(priv->mainloop = pa_threaded_mainloop_new())) {
+ mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n");
+ goto fail;
+ }
+
+ if (!(priv->context = pa_context_new(pa_threaded_mainloop_get_api(
+ priv->mainloop), PULSE_CLIENT_NAME))) {
+ mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n");
+ goto fail;
+ }
+
+ pa_context_set_state_callback(priv->context, context_state_cb, ao);
+
+ if (pa_context_connect(priv->context, host, 0, NULL) < 0)
+ goto fail;
+
+ pa_threaded_mainloop_lock(priv->mainloop);
+
+ if (pa_threaded_mainloop_start(priv->mainloop) < 0)
+ goto unlock_and_fail;
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(priv->mainloop);
+
+ if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
+ goto unlock_and_fail;
+
+ if (!(priv->stream = pa_stream_new(priv->context, "audio stream", &ss,
+ &map)))
+ goto unlock_and_fail;
+
+ pa_stream_set_state_callback(priv->stream, stream_state_cb, ao);
+ pa_stream_set_write_callback(priv->stream, stream_request_cb, ao);
+ pa_stream_set_latency_update_callback(priv->stream,
+ stream_latency_update_cb, ao);
+ pa_buffer_attr bufattr = {
+ .maxlength = -1,
+ .tlength = pa_usec_to_bytes(1000000, &ss),
+ .prebuf = -1,
+ .minreq = -1,
+ .fragsize = -1,
+ };
+ if (pa_stream_connect_playback(priv->stream, sink, &bufattr,
+ PA_STREAM_NOT_MONOTONIC, NULL, NULL) < 0)
+ goto unlock_and_fail;
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(priv->mainloop);
+
+ if (pa_stream_get_state(priv->stream) != PA_STREAM_READY)
+ goto unlock_and_fail;
+
+ pa_threaded_mainloop_unlock(priv->mainloop);
+
+ free(devarg);
+ return 0;
+
+unlock_and_fail:
+
+ if (priv->mainloop)
+ pa_threaded_mainloop_unlock(priv->mainloop);
+
+fail:
+ if (priv->context) {
+ if (!(pa_context_errno(priv->context) == PA_ERR_CONNECTIONREFUSED
+ && ao->probing))
+ GENERIC_ERR_MSG(priv->context, "Init failed");
+ }
+ free(devarg);
+ uninit(ao, true);
+ return -1;
+}
+
+static void cork(struct ao *ao, bool pause)
+{
+ struct priv *priv = ao->priv;
+ pa_threaded_mainloop_lock(priv->mainloop);
+ priv->retval = 0;
+ if (!waitop(priv, pa_stream_cork(priv->stream, pause, success_cb, ao)) ||
+ !priv->retval)
+ GENERIC_ERR_MSG(priv->context, "pa_stream_cork() failed");
+}
+
+// Play the specified data to the pulseaudio server
+static int play(struct ao *ao, void *data, int len, int flags)
+{
+ struct priv *priv = ao->priv;
+ pa_threaded_mainloop_lock(priv->mainloop);
+ if (pa_stream_write(priv->stream, data, len, NULL, 0,
+ PA_SEEK_RELATIVE) < 0) {
+ GENERIC_ERR_MSG(priv->context, "pa_stream_write() failed");
+ len = -1;
+ }
+ if (flags & AOPLAY_FINAL_CHUNK) {
+ // Force start in case the stream was too short for prebuf
+ pa_operation *op = pa_stream_trigger(priv->stream, NULL, NULL);
+ pa_operation_unref(op);
+ }
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ return len;
+}
+
+// Reset the audio stream, i.e. flush the playback buffer on the server side
+static void reset(struct ao *ao)
+{
+ // pa_stream_flush() works badly if not corked
+ cork(ao, true);
+ struct priv *priv = ao->priv;
+ pa_threaded_mainloop_lock(priv->mainloop);
+ priv->retval = 0;
+ if (!waitop(priv, pa_stream_flush(priv->stream, success_cb, ao)) ||
+ !priv->retval)
+ GENERIC_ERR_MSG(priv->context, "pa_stream_flush() failed");
+ cork(ao, false);
+}
+
+// Pause the audio stream by corking it on the server
+static void pause(struct ao *ao)
+{
+ cork(ao, true);
+}
+
+// Resume the audio stream by uncorking it on the server
+static void resume(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ /* Without this, certain versions will cause an infinite hang because
+ * pa_stream_writable_size returns 0 always.
+ * Note that this workaround causes A-V desync after pause. */
+ if (priv->broken_pause)
+ reset(ao);
+ cork(ao, false);
+}
+
+// Return number of bytes that may be written to the server without blocking
+static int get_space(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ pa_threaded_mainloop_lock(priv->mainloop);
+ size_t space = pa_stream_writable_size(priv->stream);
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ return space;
+}
+
+// Return the current latency in seconds
+static float get_delay(struct ao *ao)
+{
+ /* This code basically does what pa_stream_get_latency() _should_
+ * do, but doesn't due to multiple known bugs in PulseAudio (at
+ * PulseAudio version 2.1). In particular, the timing interpolation
+ * mode (PA_STREAM_INTERPOLATE_TIMING) can return completely bogus
+ * values, and the non-interpolating code has a bug causing too
+ * large results at end of stream (so a stream never seems to finish).
+ * This code can still return wrong values in some cases due to known
+ * PulseAudio bugs that can not be worked around on the client side.
+ *
+ * We always query the server for latest timing info. This may take
+ * too long to work well with remote audio servers, but at least
+ * this should be enough to fix the normal local playback case.
+ */
+ struct priv *priv = ao->priv;
+ pa_threaded_mainloop_lock(priv->mainloop);
+ if (!waitop(priv, pa_stream_update_timing_info(priv->stream, NULL, NULL))) {
+ GENERIC_ERR_MSG(priv->context, "pa_stream_update_timing_info() failed");
+ return 0;
+ }
+ pa_threaded_mainloop_lock(priv->mainloop);
+ const pa_timing_info *ti = pa_stream_get_timing_info(priv->stream);
+ if (!ti) {
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ GENERIC_ERR_MSG(priv->context, "pa_stream_get_timing_info() failed");
+ return 0;
+ }
+ const struct pa_sample_spec *ss = pa_stream_get_sample_spec(priv->stream);
+ if (!ss) {
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ GENERIC_ERR_MSG(priv->context, "pa_stream_get_sample_spec() failed");
+ return 0;
+ }
+ // data left in PulseAudio's main buffers (not written to sink yet)
+ int64_t latency = pa_bytes_to_usec(ti->write_index - ti->read_index, ss);
+ // since this info may be from a while ago, playback has progressed since
+ latency -= ti->transport_usec;
+ // data already moved from buffers to sink, but not played yet
+ int64_t sink_latency = ti->sink_usec;
+ if (!ti->playing)
+ /* At the end of a stream, part of the data "left" in the sink may
+ * be padding silence after the end; that should be subtracted to
+ * get the amount of real audio from our stream. This adjustment
+ * is missing from Pulseaudio's own get_latency calculations
+ * (as of PulseAudio 2.1). */
+ sink_latency -= pa_bytes_to_usec(ti->since_underrun, ss);
+ if (sink_latency > 0)
+ latency += sink_latency;
+ if (latency < 0)
+ latency = 0;
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ return latency / 1e6;
+}
+
+/* A callback function that is called when the
+ * pa_context_get_sink_input_info() operation completes. Saves the
+ * volume field of the specified structure to the global variable volume.
+ */
+static void info_func(struct pa_context *c, const struct pa_sink_input_info *i,
+ int is_last, void *userdata)
+{
+ struct ao *ao = userdata;
+ struct priv *priv = ao->priv;
+ if (is_last < 0) {
+ GENERIC_ERR_MSG(priv->context, "Failed to get sink input info");
+ return;
+ }
+ if (!i)
+ return;
+ priv->pi = *i;
+ pa_threaded_mainloop_signal(priv->mainloop, 0);
+}
+
+static int control(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ struct priv *priv = ao->priv;
+ switch (cmd) {
+ case AOCONTROL_GET_MUTE:
+ case AOCONTROL_GET_VOLUME: {
+ uint32_t devidx = pa_stream_get_index(priv->stream);
+ pa_threaded_mainloop_lock(priv->mainloop);
+ if (!waitop(priv, pa_context_get_sink_input_info(priv->context, devidx,
+ info_func, ao))) {
+ GENERIC_ERR_MSG(priv->context,
+ "pa_stream_get_sink_input_info() failed");
+ return CONTROL_ERROR;
+ }
+ // Warning: some information in pi might be unaccessible, because
+ // we naively copied the struct, without updating pointers etc.
+ // Pointers might point to invalid data, accessors might fail.
+ if (cmd == AOCONTROL_GET_VOLUME) {
+ ao_control_vol_t *vol = arg;
+ if (priv->pi.volume.channels != 2)
+ vol->left = vol->right =
+ VOL_PA2MP(pa_cvolume_avg(&priv->pi.volume));
+ else {
+ vol->left = VOL_PA2MP(priv->pi.volume.values[0]);
+ vol->right = VOL_PA2MP(priv->pi.volume.values[1]);
+ }
+ } else if (cmd == AOCONTROL_GET_MUTE) {
+ bool *mute = arg;
+ *mute = priv->pi.mute;
+ }
+ return CONTROL_OK;
+ }
+
+ case AOCONTROL_SET_MUTE:
+ case AOCONTROL_SET_VOLUME: {
+ pa_operation *o;
+
+ pa_threaded_mainloop_lock(priv->mainloop);
+ uint32_t stream_index = pa_stream_get_index(priv->stream);
+ if (cmd == AOCONTROL_SET_VOLUME) {
+ const ao_control_vol_t *vol = arg;
+ struct pa_cvolume volume;
+
+ pa_cvolume_reset(&volume, ao->channels);
+ if (volume.channels != 2)
+ pa_cvolume_set(&volume, volume.channels, VOL_MP2PA(vol->left));
+ else {
+ volume.values[0] = VOL_MP2PA(vol->left);
+ volume.values[1] = VOL_MP2PA(vol->right);
+ }
+ o = pa_context_set_sink_input_volume(priv->context, stream_index,
+ &volume, NULL, NULL);
+ if (!o) {
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ GENERIC_ERR_MSG(priv->context,
+ "pa_context_set_sink_input_volume() failed");
+ return CONTROL_ERROR;
+ }
+ } else if (cmd == AOCONTROL_SET_MUTE) {
+ const bool *mute = arg;
+ o = pa_context_set_sink_input_mute(priv->context, stream_index,
+ *mute, NULL, NULL);
+ if (!o) {
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ GENERIC_ERR_MSG(priv->context,
+ "pa_context_set_sink_input_mute() failed");
+ return CONTROL_ERROR;
+ }
+ } else
+ abort();
+ /* We don't wait for completion here */
+ pa_operation_unref(o);
+ pa_threaded_mainloop_unlock(priv->mainloop);
+ return CONTROL_OK;
+ }
+ default:
+ return CONTROL_UNKNOWN;
+ }
+}
+
+const struct ao_driver audio_out_pulse = {
+ .is_new = true,
+ .info = &(const struct ao_info) {
+ "PulseAudio audio output",
+ "pulse",
+ "Lennart Poettering",
+ "",
+ },
+ .control = control,
+ .init = init,
+ .uninit = uninit,
+ .reset = reset,
+ .get_space = get_space,
+ .play = play,
+ .get_delay = get_delay,
+ .pause = pause,
+ .resume = resume,
+};
diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c
new file mode 100644
index 0000000000..8232aad865
--- /dev/null
+++ b/audio/out/ao_rsound.c
@@ -0,0 +1,214 @@
+/*
+ * RSound audio output driver
+ *
+ * Copyright (C) 2011 Hans-Kristian Arntzen
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <rsound.h>
+
+#include "talloc.h"
+
+#include "subopt-helper.h"
+#include "osdep/timer.h"
+#include "libaf/format.h"
+#include "audio_out.h"
+
+struct priv {
+ rsound_t *rd;
+};
+
+static int set_format(struct ao *ao)
+{
+ int rsd_format;
+
+ switch (ao->format) {
+ case AF_FORMAT_U8:
+ rsd_format = RSD_U8;
+ break;
+ case AF_FORMAT_S8:
+ rsd_format = RSD_S8;
+ break;
+ case AF_FORMAT_S16_LE:
+ rsd_format = RSD_S16_LE;
+ break;
+ case AF_FORMAT_S16_BE:
+ rsd_format = RSD_S16_BE;
+ break;
+ case AF_FORMAT_U16_LE:
+ rsd_format = RSD_U16_LE;
+ break;
+ case AF_FORMAT_U16_BE:
+ rsd_format = RSD_U16_BE;
+ break;
+ case AF_FORMAT_S24_LE:
+ case AF_FORMAT_S24_BE:
+ case AF_FORMAT_U24_LE:
+ case AF_FORMAT_U24_BE:
+ rsd_format = RSD_S32_LE;
+ ao->format = AF_FORMAT_S32_LE;
+ break;
+ case AF_FORMAT_S32_LE:
+ rsd_format = RSD_S32_LE;
+ break;
+ case AF_FORMAT_S32_BE:
+ rsd_format = RSD_S32_BE;
+ break;
+ case AF_FORMAT_U32_LE:
+ rsd_format = RSD_U32_LE;
+ break;
+ case AF_FORMAT_U32_BE:
+ rsd_format = RSD_U32_BE;
+ break;
+ case AF_FORMAT_A_LAW:
+ rsd_format = RSD_ALAW;
+ break;
+ case AF_FORMAT_MU_LAW:
+ rsd_format = RSD_MULAW;
+ break;
+ default:
+ rsd_format = RSD_S16_LE;
+ ao->format = AF_FORMAT_S16_LE;
+ }
+
+ return rsd_format;
+}
+
+static int init(struct ao *ao, char *params)
+{
+ struct priv *priv = talloc_zero(ao, struct priv);
+ ao->priv = priv;
+
+ char *host = NULL;
+ char *port = NULL;
+
+ const opt_t subopts[] = {
+ {"host", OPT_ARG_MSTRZ, &host, NULL},
+ {"port", OPT_ARG_MSTRZ, &port, NULL},
+ {NULL}
+ };
+
+ if (subopt_parse(params, subopts) != 0)
+ return -1;
+
+ if (rsd_init(&priv->rd) < 0) {
+ free(host);
+ free(port);
+ return -1;
+ }
+
+ if (host) {
+ rsd_set_param(priv->rd, RSD_HOST, host);
+ free(host);
+ }
+
+ if (port) {
+ rsd_set_param(priv->rd, RSD_PORT, port);
+ free(port);
+ }
+
+ rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate);
+ rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels);
+
+ int rsd_format = set_format(ao);
+ rsd_set_param(priv->rd, RSD_FORMAT, &rsd_format);
+
+ if (rsd_start(priv->rd) < 0) {
+ rsd_free(priv->rd);
+ return -1;
+ }
+
+ ao->bps = ao->channels * ao->samplerate * af_fmt2bits(ao->format) / 8;
+
+ return 0;
+}
+
+static void uninit(struct ao *ao, bool cut_audio)
+{
+ struct priv *priv = ao->priv;
+ /* The API does not provide a direct way to explicitly wait until
+ * the last byte has been played server-side as this cannot be
+ * guaranteed by backend drivers, so we approximate this behavior.
+ */
+ if (!cut_audio)
+ usec_sleep(rsd_delay_ms(priv->rd) * 1000);
+
+ rsd_stop(priv->rd);
+ rsd_free(priv->rd);
+}
+
+static void reset(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ rsd_stop(priv->rd);
+ rsd_start(priv->rd);
+}
+
+static void audio_pause(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ rsd_pause(priv->rd, 1);
+}
+
+static void audio_resume(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ rsd_pause(priv->rd, 0);
+}
+
+static int get_space(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ return rsd_get_avail(priv->rd);
+}
+
+static int play(struct ao *ao, void *data, int len, int flags)
+{
+ struct priv *priv = ao->priv;
+ return rsd_write(priv->rd, data, len);
+}
+
+static float get_delay(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ return rsd_delay_ms(priv->rd) / 1000.0;
+}
+
+const struct ao_driver audio_out_rsound = {
+ .is_new = true,
+ .info = &(const struct ao_info) {
+ .name = "RSound output driver",
+ .short_name = "rsound",
+ .author = "Hans-Kristian Arntzen",
+ .comment = "",
+ },
+ .init = init,
+ .uninit = uninit,
+ .reset = reset,
+ .get_space = get_space,
+ .play = play,
+ .get_delay = get_delay,
+ .pause = audio_pause,
+ .resume = audio_resume,
+};
+
diff --git a/audio/out/audio_out_internal.h b/audio/out/audio_out_internal.h
new file mode 100644
index 0000000000..215428fb0e
--- /dev/null
+++ b/audio/out/audio_out_internal.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_AUDIO_OUT_INTERNAL_H
+#define MPLAYER_AUDIO_OUT_INTERNAL_H
+
+#include "options.h"
+
+// prototypes:
+//static ao_info_t info;
+static int control(int cmd, void *arg);
+static int init(int rate,int channels,int format,int flags);
+static void uninit(int immed);
+static void reset(void);
+static int get_space(void);
+static int play(void* data,int len,int flags);
+static float get_delay(void);
+static void audio_pause(void);
+static void audio_resume(void);
+
+extern struct ao *global_ao;
+#define ao_data (*global_ao)
+#define mixer_channel (global_ao->opts->mixer_channel)
+#define mixer_device (global_ao->opts->mixer_device)
+
+#define LIBAO_EXTERN(x) const struct ao_driver audio_out_##x = { \
+ .info = &info, \
+ .control = old_ao_control, \
+ .init = old_ao_init, \
+ .uninit = old_ao_uninit, \
+ .reset = old_ao_reset, \
+ .get_space = old_ao_get_space, \
+ .play = old_ao_play, \
+ .get_delay = old_ao_get_delay, \
+ .pause = old_ao_pause, \
+ .resume = old_ao_resume, \
+ .old_functions = &(const struct ao_old_functions) { \
+ .control = control, \
+ .init = init, \
+ .uninit = uninit, \
+ .reset = reset, \
+ .get_space = get_space, \
+ .play = play, \
+ .get_delay = get_delay, \
+ .pause = audio_pause, \
+ .resume = audio_resume, \
+ }, \
+};
+
+#endif /* MPLAYER_AUDIO_OUT_INTERNAL_H */
diff --git a/audio/reorder_ch.c b/audio/reorder_ch.c
new file mode 100644
index 0000000000..50379de2c5
--- /dev/null
+++ b/audio/reorder_ch.c
@@ -0,0 +1,1400 @@
+/*
+ * common functions for reordering audio channels
+ *
+ * Copyright (C) 2007 Ulion <ulion A gmail P com>
+ *
+ * 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 <inttypes.h>
+#include <string.h>
+
+#include "reorder_ch.h"
+
+#ifdef TEST
+#define mp_msg(mod,lev, fmt, args... ) printf( fmt, ## args )
+#else
+#include "mp_msg.h"
+#endif
+
+
+#define REORDER_COPY_5(DEST,SRC,SAMPLES,S0,S1,S2,S3,S4) \
+for (i = 0; i < SAMPLES; i += 5) {\
+ DEST[i] = SRC[i+S0];\
+ DEST[i+1] = SRC[i+S1];\
+ DEST[i+2] = SRC[i+S2];\
+ DEST[i+3] = SRC[i+S3];\
+ DEST[i+4] = SRC[i+S4];\
+}
+
+static int reorder_copy_5ch(void *dest, const void *src,
+ unsigned int samples, unsigned int samplesize,
+ int s0, int s1, int s2, int s3, int s4)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *dest_8 = dest;
+ const int8_t *src_8 = src;
+ REORDER_COPY_5(dest_8,src_8,samples,s0,s1,s2,s3,s4);
+ break;
+ }
+ case 2:
+ {
+ int16_t *dest_16 = dest;
+ const int16_t *src_16 = src;
+ REORDER_COPY_5(dest_16,src_16,samples,s0,s1,s2,s3,s4);
+ break;
+ }
+ case 3:
+ {
+ int8_t *dest_8 = dest;
+ const int8_t *src_8 = src;
+ for (i = 0; i < samples * 3; i += 15) {
+ dest_8[i] = src_8[i+s0*3];
+ dest_8[i+1] = src_8[i+s0*3+1];
+ dest_8[i+2] = src_8[i+s0*3+2];
+ dest_8[i+3] = src_8[i+s1*3];
+ dest_8[i+4] = src_8[i+s1*3+1];
+ dest_8[i+5] = src_8[i+s1*3+2];
+ dest_8[i+6] = src_8[i+s2*3];
+ dest_8[i+7] = src_8[i+s2*3+1];
+ dest_8[i+8] = src_8[i+s2*3+2];
+ dest_8[i+9] = src_8[i+s3*3];
+ dest_8[i+10] = src_8[i+s3*3+1];
+ dest_8[i+11] = src_8[i+s3*3+2];
+ dest_8[i+12] = src_8[i+s4*3];
+ dest_8[i+13] = src_8[i+s4*3+1];
+ dest_8[i+14] = src_8[i+s4*3+2];
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *dest_32 = dest;
+ const int32_t *src_32 = src;
+ REORDER_COPY_5(dest_32,src_32,samples,s0,s1,s2,s3,s4);
+ break;
+ }
+ case 8:
+ {
+ int64_t *dest_64 = dest;
+ const int64_t *src_64 = src;
+ REORDER_COPY_5(dest_64,src_64,samples,s0,s1,s2,s3,s4);
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_COPY_6(DEST,SRC,SAMPLES,S0,S1,S2,S3,S4,S5) \
+for (i = 0; i < SAMPLES; i += 6) {\
+ DEST[i] = SRC[i+S0];\
+ DEST[i+1] = SRC[i+S1];\
+ DEST[i+2] = SRC[i+S2];\
+ DEST[i+3] = SRC[i+S3];\
+ DEST[i+4] = SRC[i+S4];\
+ DEST[i+5] = SRC[i+S5];\
+}
+
+static int reorder_copy_6ch(void *dest, const void *src,
+ unsigned int samples, uint8_t samplesize,
+ int s0, int s1, int s2, int s3, int s4, int s5)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *dest_8 = dest;
+ const int8_t *src_8 = src;
+ REORDER_COPY_6(dest_8,src_8,samples,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ case 2:
+ {
+ int16_t *dest_16 = dest;
+ const int16_t *src_16 = src;
+ REORDER_COPY_6(dest_16,src_16,samples,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ case 3:
+ {
+ int8_t *dest_8 = dest;
+ const int8_t *src_8 = src;
+ for (i = 0; i < samples * 3; i += 18) {
+ dest_8[i] = src_8[i+s0*3];
+ dest_8[i+1] = src_8[i+s0*3+1];
+ dest_8[i+2] = src_8[i+s0*3+2];
+ dest_8[i+3] = src_8[i+s1*3];
+ dest_8[i+4] = src_8[i+s1*3+1];
+ dest_8[i+5] = src_8[i+s1*3+2];
+ dest_8[i+6] = src_8[i+s2*3];
+ dest_8[i+7] = src_8[i+s2*3+1];
+ dest_8[i+8] = src_8[i+s2*3+2];
+ dest_8[i+9] = src_8[i+s3*3];
+ dest_8[i+10] = src_8[i+s3*3+1];
+ dest_8[i+11] = src_8[i+s3*3+2];
+ dest_8[i+12] = src_8[i+s4*3];
+ dest_8[i+13] = src_8[i+s4*3+1];
+ dest_8[i+14] = src_8[i+s4*3+2];
+ dest_8[i+15] = src_8[i+s5*3];
+ dest_8[i+16] = src_8[i+s5*3+1];
+ dest_8[i+17] = src_8[i+s5*3+2];
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *dest_32 = dest;
+ const int32_t *src_32 = src;
+ REORDER_COPY_6(dest_32,src_32,samples,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ case 8:
+ {
+ int64_t *dest_64 = dest;
+ const int64_t *src_64 = src;
+ REORDER_COPY_6(dest_64,src_64,samples,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_COPY_8(DEST,SRC,SAMPLES,S0,S1,S2,S3,S4,S5,S6,S7) \
+for (i = 0; i < SAMPLES; i += 8) {\
+ DEST[i] = SRC[i+S0];\
+ DEST[i+1] = SRC[i+S1];\
+ DEST[i+2] = SRC[i+S2];\
+ DEST[i+3] = SRC[i+S3];\
+ DEST[i+4] = SRC[i+S4];\
+ DEST[i+5] = SRC[i+S5];\
+ DEST[i+6] = SRC[i+S6];\
+ DEST[i+7] = SRC[i+S7];\
+}
+
+static int reorder_copy_8ch(void *dest, const void *src,
+ unsigned int samples, uint8_t samplesize,
+ int s0, int s1, int s2, int s3,
+ int s4, int s5, int s6, int s7)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *dest_8 = dest;
+ const int8_t *src_8 = src;
+ REORDER_COPY_8(dest_8,src_8,samples,s0,s1,s2,s3,s4,s5,s6,s7);
+ break;
+ }
+ case 2:
+ {
+ int16_t *dest_16 = dest;
+ const int16_t *src_16 = src;
+ REORDER_COPY_8(dest_16,src_16,samples,s0,s1,s2,s3,s4,s5,s6,s7);
+ break;
+ }
+ case 3:
+ {
+ int8_t *dest_8 = dest;
+ const int8_t *src_8 = src;
+ for (i = 0; i < samples * 3; i += 24) {
+ dest_8[i] = src_8[i+s0*3];
+ dest_8[i+1] = src_8[i+s0*3+1];
+ dest_8[i+2] = src_8[i+s0*3+2];
+ dest_8[i+3] = src_8[i+s1*3];
+ dest_8[i+4] = src_8[i+s1*3+1];
+ dest_8[i+5] = src_8[i+s1*3+2];
+ dest_8[i+6] = src_8[i+s2*3];
+ dest_8[i+7] = src_8[i+s2*3+1];
+ dest_8[i+8] = src_8[i+s2*3+2];
+ dest_8[i+9] = src_8[i+s3*3];
+ dest_8[i+10] = src_8[i+s3*3+1];
+ dest_8[i+11] = src_8[i+s3*3+2];
+ dest_8[i+12] = src_8[i+s4*3];
+ dest_8[i+13] = src_8[i+s4*3+1];
+ dest_8[i+14] = src_8[i+s4*3+2];
+ dest_8[i+15] = src_8[i+s5*3];
+ dest_8[i+16] = src_8[i+s5*3+1];
+ dest_8[i+17] = src_8[i+s5*3+2];
+ dest_8[i+18] = src_8[i+s6*3];
+ dest_8[i+19] = src_8[i+s6*3+1];
+ dest_8[i+20] = src_8[i+s6*3+2];
+ dest_8[i+21] = src_8[i+s7*3];
+ dest_8[i+22] = src_8[i+s7*3+1];
+ dest_8[i+23] = src_8[i+s7*3+2];
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *dest_32 = dest;
+ const int32_t *src_32 = src;
+ REORDER_COPY_8(dest_32,src_32,samples,s0,s1,s2,s3,s4,s5,s6,s7);
+ break;
+ }
+ case 8:
+ {
+ int64_t *dest_64 = dest;
+ const int64_t *src_64 = src;
+ REORDER_COPY_8(dest_64,src_64,samples,s0,s1,s2,s3,s4,s5,s6,s7);
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+void reorder_channel_copy(void *src,
+ int src_layout,
+ void *dest,
+ int dest_layout,
+ int samples,
+ int samplesize)
+{
+ if (dest_layout==src_layout) {
+ memcpy(dest, src, samples*samplesize);
+ return;
+ }
+ if (!AF_IS_SAME_CH_NUM(dest_layout,src_layout)) {
+ mp_msg(MSGT_GLOBAL, MSGL_WARN, "[reorder_ch] different channel count "
+ "between src and dest: %x, %x\n",
+ AF_GET_CH_NUM_WITH_LFE(src_layout),
+ AF_GET_CH_NUM_WITH_LFE(dest_layout));
+ return;
+ }
+ switch ((src_layout<<16)|dest_layout) {
+ // AF_CHANNEL_LAYOUT_5_0_A L R C Ls Rs
+ // AF_CHANNEL_LAYOUT_5_0_B L R Ls Rs C
+ // AF_CHANNEL_LAYOUT_5_0_C L C R Ls Rs
+ // AF_CHANNEL_LAYOUT_5_0_D C L R Ls Rs
+ case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_B:
+ reorder_copy_5ch(dest, src, samples, samplesize, 0, 1, 3, 4, 2);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_C:
+ reorder_copy_5ch(dest, src, samples, samplesize, 0, 2, 1, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_D:
+ reorder_copy_5ch(dest, src, samples, samplesize, 2, 0, 1, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_A:
+ reorder_copy_5ch(dest, src, samples, samplesize, 0, 1, 4, 2, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_C:
+ reorder_copy_5ch(dest, src, samples, samplesize, 0, 4, 1, 2, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_D:
+ reorder_copy_5ch(dest, src, samples, samplesize, 4, 0, 1, 2, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_A:
+ reorder_copy_5ch(dest, src, samples, samplesize, 0, 2, 1, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_B:
+ reorder_copy_5ch(dest, src, samples, samplesize, 0, 2, 3, 4, 1);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_D:
+ reorder_copy_5ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_A:
+ reorder_copy_5ch(dest, src, samples, samplesize, 1, 2, 0, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_B:
+ reorder_copy_5ch(dest, src, samples, samplesize, 1, 2, 3, 4, 0);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_C:
+ reorder_copy_5ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4);
+ break;
+ // AF_CHANNEL_LAYOUT_5_1_A L R C LFE Ls Rs
+ // AF_CHANNEL_LAYOUT_5_1_B L R Ls Rs C LFE
+ // AF_CHANNEL_LAYOUT_5_1_C L C R Ls Rs LFE
+ // AF_CHANNEL_LAYOUT_5_1_D C L R Ls Rs LFE
+ // AF_CHANNEL_LAYOUT_5_1_E LFE L C R Ls Rs
+ case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_copy_6ch(dest, src, samples, samplesize, 0, 1, 4, 5, 2, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_C:
+ reorder_copy_6ch(dest, src, samples, samplesize, 0, 2, 1, 4, 5, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_D:
+ reorder_copy_6ch(dest, src, samples, samplesize, 2, 0, 1, 4, 5, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_A:
+ reorder_copy_6ch(dest, src, samples, samplesize, 0, 1, 4, 5, 2, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_C:
+ reorder_copy_6ch(dest, src, samples, samplesize, 0, 4, 1, 2, 3, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_D:
+ reorder_copy_6ch(dest, src, samples, samplesize, 4, 0, 1, 2, 3, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_E:
+ reorder_copy_6ch(dest, src, samples, samplesize, 5, 0, 4, 1, 2, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_A:
+ reorder_copy_6ch(dest, src, samples, samplesize, 0, 2, 1, 5, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_copy_6ch(dest, src, samples, samplesize, 0, 2, 3, 4, 1, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_D:
+ reorder_copy_6ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_A:
+ reorder_copy_6ch(dest, src, samples, samplesize, 1, 2, 0, 5, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_copy_6ch(dest, src, samples, samplesize, 1, 2, 3, 4, 0, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_C:
+ reorder_copy_6ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_E << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_copy_6ch(dest, src, samples, samplesize, 1, 3, 4, 5, 2, 0);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_F << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_copy_6ch(dest, src, samples, samplesize, 1, 2, 4, 5, 0, 3);
+ break;
+ // AF_CHANNEL_LAYOUT_7_1_A L R C LFE Ls Rs Rls Rrs
+ // AF_CHANNEL_LAYOUT_7_1_B L R Ls Rs C LFE Rls Rrs
+ // AF_CHANNEL_LAYOUT_7_1_D C L R Ls Rs Rls Rrs LFE
+ case AF_CHANNEL_LAYOUT_7_1_A << 16 | AF_CHANNEL_LAYOUT_7_1_B:
+ case AF_CHANNEL_LAYOUT_7_1_B << 16 | AF_CHANNEL_LAYOUT_7_1_A:
+ reorder_copy_8ch(dest, src, samples, samplesize, 0, 1, 4, 5, 2, 3, 6, 7);
+ break;
+ case AF_CHANNEL_LAYOUT_7_1_D << 16 | AF_CHANNEL_LAYOUT_7_1_B:
+ reorder_copy_8ch(dest, src, samples, samplesize, 1, 2, 3, 4, 0, 7, 5, 6);
+ break;
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN, "[reorder_channel_copy] unsupport "
+ "from %x to %x, %d * %d\n", src_layout, dest_layout,
+ samples, samplesize);
+ memcpy(dest, src, samples*samplesize);
+ }
+}
+
+
+#define REORDER_SELF_SWAP_2(SRC,TMP,SAMPLES,CHNUM,S0,S1) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S1];\
+ SRC[i+S1] = TMP;\
+}
+
+static int reorder_self_2(void *src, unsigned int samples,
+ unsigned int samplesize, unsigned int chnum,
+ int s0, int s1)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2(src_8,tmp,samples,6,s0,s1);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_2(src_8,tmp,samples,8,s0,s1);
+ }
+ else {
+ REORDER_SELF_SWAP_2(src_8,tmp,samples,5,s0,s1);
+ }
+ break;
+ }
+ case 2:
+ {
+ int16_t *src_16 = src;
+ int16_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2(src_16,tmp,samples,6,s0,s1);
+ }
+ else if (chnum==3) {
+ REORDER_SELF_SWAP_2(src_16,tmp,samples,3,s0,s1);
+ }
+ else if (chnum==4) {
+ REORDER_SELF_SWAP_2(src_16,tmp,samples,3,s0,s1);
+ }
+ else {
+ REORDER_SELF_SWAP_2(src_16,tmp,samples,5,s0,s1);
+ }
+ break;
+ }
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += chnum * 3) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s1*3];
+ src_8[i+s0*3+1] = src_8[i+s1*3+1];
+ src_8[i+s0*3+2] = src_8[i+s1*3+2];
+ src_8[i+s1*3] = tmp0;
+ src_8[i+s1*3+1] = tmp1;
+ src_8[i+s1*3+2] = tmp2;
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *src_32 = src;
+ int32_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2(src_32,tmp,samples,6,s0,s1);
+ }
+ else if (chnum==3) {
+ REORDER_SELF_SWAP_2(src_32,tmp,samples,3,s0,s1);
+ }
+ else if (chnum==4) {
+ REORDER_SELF_SWAP_2(src_32,tmp,samples,4,s0,s1);
+ }
+ else {
+ REORDER_SELF_SWAP_2(src_32,tmp,samples,5,s0,s1);
+ }
+ break;
+ }
+ case 8:
+ {
+ int64_t *src_64 = src;
+ int64_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2(src_64,tmp,samples,6,s0,s1);
+ }
+ else if (chnum==3) {
+ REORDER_SELF_SWAP_2(src_64,tmp,samples,3,s0,s1);
+ }
+ else if (chnum==4) {
+ REORDER_SELF_SWAP_2(src_64,tmp,samples,4,s0,s1);
+ }
+ else {
+ REORDER_SELF_SWAP_2(src_64,tmp,samples,5,s0,s1);
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_SELF_SWAP_3(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S1];\
+ SRC[i+S1] = SRC[i+S2];\
+ SRC[i+S2] = TMP;\
+}
+
+static int reorder_self_3(void *src, unsigned int samples,
+ unsigned int samplesize, unsigned int chnum,
+ int s0, int s1, int s2)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_3(src_8,tmp,samples,6,s0,s1,s2);
+ }
+ else {
+ REORDER_SELF_SWAP_3(src_8,tmp,samples,5,s0,s1,s2);
+ }
+ break;
+ }
+ case 2:
+ {
+ int16_t *src_16 = src;
+ int16_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_3(src_16,tmp,samples,6,s0,s1,s2);
+ }
+ else {
+ REORDER_SELF_SWAP_3(src_16,tmp,samples,5,s0,s1,s2);
+ }
+ break;
+ }
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += chnum * 3) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s1*3];
+ src_8[i+s0*3+1] = src_8[i+s1*3+1];
+ src_8[i+s0*3+2] = src_8[i+s1*3+2];
+ src_8[i+s1*3] = src_8[i+s2*3];
+ src_8[i+s1*3+1] = src_8[i+s2*3+1];
+ src_8[i+s1*3+2] = src_8[i+s2*3+2];
+ src_8[i+s2*3] = tmp0;
+ src_8[i+s2*3+1] = tmp1;
+ src_8[i+s2*3+2] = tmp2;
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *src_32 = src;
+ int32_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_3(src_32,tmp,samples,6,s0,s1,s2);
+ }
+ else {
+ REORDER_SELF_SWAP_3(src_32,tmp,samples,5,s0,s1,s2);
+ }
+ break;
+ }
+ case 8:
+ {
+ int64_t *src_64 = src;
+ int64_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_3(src_64,tmp,samples,6,s0,s1,s2);
+ }
+ else {
+ REORDER_SELF_SWAP_3(src_64,tmp,samples,5,s0,s1,s2);
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_SELF_SWAP_4_STEP_1(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S1];\
+ SRC[i+S1] = SRC[i+S2];\
+ SRC[i+S2] = SRC[i+S3];\
+ SRC[i+S3] = TMP;\
+}
+
+static int reorder_self_4_step_1(void *src, unsigned int samples,
+ unsigned int samplesize, unsigned int chnum,
+ int s0, int s1, int s2, int s3)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_4_STEP_1(src_8,tmp,samples,6,s0,s1,s2,s3);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_4_STEP_1(src_8,tmp,samples,8,s0,s1,s2,s3);
+ }
+ else {
+ REORDER_SELF_SWAP_4_STEP_1(src_8,tmp,samples,5,s0,s1,s2,s3);
+ }
+ break;
+ }
+ case 2:
+ {
+ int16_t *src_16 = src;
+ int16_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_4_STEP_1(src_16,tmp,samples,6,s0,s1,s2,s3);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_4_STEP_1(src_16,tmp,samples,8,s0,s1,s2,s3);
+ }
+ else {
+ REORDER_SELF_SWAP_4_STEP_1(src_16,tmp,samples,5,s0,s1,s2,s3);
+ }
+ break;
+ }
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += chnum * 3) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s1*3];
+ src_8[i+s0*3+1] = src_8[i+s1*3+1];
+ src_8[i+s0*3+2] = src_8[i+s1*3+2];
+ src_8[i+s1*3] = src_8[i+s2*3];
+ src_8[i+s1*3+1] = src_8[i+s2*3+1];
+ src_8[i+s1*3+2] = src_8[i+s2*3+2];
+ src_8[i+s2*3] = src_8[i+s3*3];
+ src_8[i+s2*3+1] = src_8[i+s3*3+1];
+ src_8[i+s2*3+2] = src_8[i+s3*3+2];
+ src_8[i+s3*3] = tmp0;
+ src_8[i+s3*3+1] = tmp1;
+ src_8[i+s3*3+2] = tmp2;
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *src_32 = src;
+ int32_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_4_STEP_1(src_32,tmp,samples,6,s0,s1,s2,s3);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_4_STEP_1(src_32,tmp,samples,8,s0,s1,s2,s3);
+ }
+ else {
+ REORDER_SELF_SWAP_4_STEP_1(src_32,tmp,samples,5,s0,s1,s2,s3);
+ }
+ break;
+ }
+ case 8:
+ {
+ int64_t *src_64 = src;
+ int64_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_4_STEP_1(src_64,tmp,samples,6,s0,s1,s2,s3);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_4_STEP_1(src_64,tmp,samples,8,s0,s1,s2,s3);
+ }
+ else {
+ REORDER_SELF_SWAP_4_STEP_1(src_64,tmp,samples,5,s0,s1,s2,s3);
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_SELF_SWAP_4_STEP_2(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S2];\
+ SRC[i+S2] = TMP;\
+ TMP = SRC[i+S1];\
+ SRC[i+S1] = SRC[i+S3];\
+ SRC[i+S3] = TMP;\
+}
+
+static int reorder_self_4_step_2(void *src, unsigned int samples,
+ unsigned int samplesize, unsigned int chnum,
+ int s0, int s1, int s2, int s3)
+{
+ int i;
+ switch (samplesize) {
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += chnum * 3) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s2*3];
+ src_8[i+s0*3+1] = src_8[i+s2*3+1];
+ src_8[i+s0*3+2] = src_8[i+s2*3+2];
+ src_8[i+s2*3] = tmp0;
+ src_8[i+s2*3+1] = tmp1;
+ src_8[i+s2*3+2] = tmp2;
+ tmp0 = src_8[i+s1*3];
+ tmp1 = src_8[i+s1*3+1];
+ tmp2 = src_8[i+s1*3+2];
+ src_8[i+s1*3] = src_8[i+s3*3];
+ src_8[i+s1*3+1] = src_8[i+s3*3+1];
+ src_8[i+s1*3+2] = src_8[i+s3*3+2];
+ src_8[i+s3*3] = tmp0;
+ src_8[i+s3*3+1] = tmp1;
+ src_8[i+s3*3+2] = tmp2;
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_SELF_SWAP_5_STEP_1(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S1];\
+ SRC[i+S1] = SRC[i+S2];\
+ SRC[i+S2] = SRC[i+S3];\
+ SRC[i+S3] = SRC[i+S4];\
+ SRC[i+S4] = TMP;\
+}
+
+static int reorder_self_5_step_1(void *src, unsigned int samples,
+ unsigned int samplesize, unsigned int chnum,
+ int s0, int s1, int s2, int s3, int s4)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_5_STEP_1(src_8,tmp,samples,6,s0,s1,s2,s3,s4);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_5_STEP_1(src_8,tmp,samples,8,s0,s1,s2,s3,s4);
+ }
+ else {
+ REORDER_SELF_SWAP_5_STEP_1(src_8,tmp,samples,5,s0,s1,s2,s3,s4);
+ }
+ break;
+ }
+ case 2:
+ {
+ int16_t *src_16 = src;
+ int16_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_5_STEP_1(src_16,tmp,samples,6,s0,s1,s2,s3,s4);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_5_STEP_1(src_16,tmp,samples,8,s0,s1,s2,s3,s4);
+ }
+ else {
+ REORDER_SELF_SWAP_5_STEP_1(src_16,tmp,samples,5,s0,s1,s2,s3,s4);
+ }
+ break;
+ }
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += chnum * 3) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s1*3];
+ src_8[i+s0*3+1] = src_8[i+s1*3+1];
+ src_8[i+s0*3+2] = src_8[i+s1*3+2];
+ src_8[i+s1*3] = src_8[i+s2*3];
+ src_8[i+s1*3+1] = src_8[i+s2*3+1];
+ src_8[i+s1*3+2] = src_8[i+s2*3+2];
+ src_8[i+s2*3] = src_8[i+s3*3];
+ src_8[i+s2*3+1] = src_8[i+s3*3+1];
+ src_8[i+s2*3+2] = src_8[i+s3*3+2];
+ src_8[i+s3*3] = src_8[i+s4*3];
+ src_8[i+s3*3+1] = src_8[i+s4*3+1];
+ src_8[i+s3*3+2] = src_8[i+s4*3+2];
+ src_8[i+s4*3] = tmp0;
+ src_8[i+s4*3+1] = tmp1;
+ src_8[i+s4*3+2] = tmp2;
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *src_32 = src;
+ int32_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_5_STEP_1(src_32,tmp,samples,6,s0,s1,s2,s3,s4);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_5_STEP_1(src_32,tmp,samples,8,s0,s1,s2,s3,s4);
+ }
+ else {
+ REORDER_SELF_SWAP_5_STEP_1(src_32,tmp,samples,5,s0,s1,s2,s3,s4);
+ }
+ break;
+ }
+ case 8:
+ {
+ int64_t *src_64 = src;
+ int64_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_5_STEP_1(src_64,tmp,samples,6,s0,s1,s2,s3,s4);
+ }
+ else if (chnum==8) {
+ REORDER_SELF_SWAP_5_STEP_1(src_64,tmp,samples,8,s0,s1,s2,s3,s4);
+ }
+ else {
+ REORDER_SELF_SWAP_5_STEP_1(src_64,tmp,samples,5,s0,s1,s2,s3,s4);
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_SELF_SWAP_2_3(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S1];\
+ SRC[i+S1] = TMP;\
+ TMP = SRC[i+S2];\
+ SRC[i+S2] = SRC[i+S3];\
+ SRC[i+S3] = SRC[i+S4];\
+ SRC[i+S4] = TMP;\
+}
+
+static int reorder_self_2_3(void *src, unsigned int samples,
+ unsigned int samplesize,
+ int s0, int s1, int s2, int s3, int s4)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp;
+ REORDER_SELF_SWAP_2_3(src_8,tmp,samples,6,s0,s1,s2,s3,s4);
+ break;
+ }
+ case 2:
+ {
+ int16_t *src_16 = src;
+ int16_t tmp;
+ REORDER_SELF_SWAP_2_3(src_16,tmp,samples,6,s0,s1,s2,s3,s4);
+ break;
+ }
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += 18) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s1*3];
+ src_8[i+s0*3+1] = src_8[i+s1*3+1];
+ src_8[i+s0*3+2] = src_8[i+s1*3+2];
+ src_8[i+s1*3] = tmp0;
+ src_8[i+s1*3+1] = tmp1;
+ src_8[i+s1*3+2] = tmp2;
+ tmp0 = src_8[i+s2*3];
+ tmp1 = src_8[i+s2*3+1];
+ tmp2 = src_8[i+s2*3+2];
+ src_8[i+s2*3] = src_8[i+s3*3];
+ src_8[i+s2*3+1] = src_8[i+s3*3+1];
+ src_8[i+s2*3+2] = src_8[i+s3*3+2];
+ src_8[i+s3*3] = src_8[i+s4*3];
+ src_8[i+s3*3+1] = src_8[i+s4*3+1];
+ src_8[i+s3*3+2] = src_8[i+s4*3+2];
+ src_8[i+s4*3] = tmp0;
+ src_8[i+s4*3+1] = tmp1;
+ src_8[i+s4*3+2] = tmp2;
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *src_32 = src;
+ int32_t tmp;
+ REORDER_SELF_SWAP_2_3(src_32,tmp,samples,6,s0,s1,s2,s3,s4);
+ break;
+ }
+ case 8:
+ {
+ int64_t *src_64 = src;
+ int64_t tmp;
+ REORDER_SELF_SWAP_2_3(src_64,tmp,samples,6,s0,s1,s2,s3,s4);
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_SELF_SWAP_3_3(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4,S5) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S1];\
+ SRC[i+S1] = SRC[i+S2];\
+ SRC[i+S2] = TMP;\
+ TMP = SRC[i+S3];\
+ SRC[i+S3] = SRC[i+S4];\
+ SRC[i+S4] = SRC[i+S5];\
+ SRC[i+S5] = TMP;\
+}
+
+static int reorder_self_3_3(void *src, unsigned int samples,
+ unsigned int samplesize,
+ int s0, int s1, int s2, int s3, int s4, int s5)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp;
+ REORDER_SELF_SWAP_3_3(src_8,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ case 2:
+ {
+ int16_t *src_16 = src;
+ int16_t tmp;
+ REORDER_SELF_SWAP_3_3(src_16,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += 18) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s1*3];
+ src_8[i+s0*3+1] = src_8[i+s1*3+1];
+ src_8[i+s0*3+2] = src_8[i+s1*3+2];
+ src_8[i+s1*3] = src_8[i+s2*3];
+ src_8[i+s1*3+1] = src_8[i+s2*3+1];
+ src_8[i+s1*3+2] = src_8[i+s2*3+2];
+ src_8[i+s2*3] = tmp0;
+ src_8[i+s2*3+1] = tmp1;
+ src_8[i+s2*3+2] = tmp2;
+ tmp0 = src_8[i+s3*3];
+ tmp1 = src_8[i+s3*3+1];
+ tmp2 = src_8[i+s3*3+2];
+ src_8[i+s3*3] = src_8[i+s4*3];
+ src_8[i+s3*3+1] = src_8[i+s4*3+1];
+ src_8[i+s3*3+2] = src_8[i+s4*3+2];
+ src_8[i+s4*3] = src_8[i+s5*3];
+ src_8[i+s4*3+1] = src_8[i+s5*3+1];
+ src_8[i+s4*3+2] = src_8[i+s5*3+2];
+ src_8[i+s5*3] = tmp0;
+ src_8[i+s5*3+1] = tmp1;
+ src_8[i+s5*3+2] = tmp2;
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *src_32 = src;
+ int32_t tmp;
+ REORDER_SELF_SWAP_3_3(src_32,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ case 8:
+ {
+ int64_t *src_64 = src;
+ int64_t tmp;
+ REORDER_SELF_SWAP_3_3(src_64,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+#define REORDER_SELF_SWAP_2_4(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4,S5) \
+for (i = 0; i < SAMPLES; i += CHNUM) {\
+ TMP = SRC[i+S0];\
+ SRC[i+S0] = SRC[i+S1];\
+ SRC[i+S1] = TMP;\
+ TMP = SRC[i+S2];\
+ SRC[i+S2] = SRC[i+S3];\
+ SRC[i+S3] = SRC[i+S4];\
+ SRC[i+S4] = SRC[i+S5];\
+ SRC[i+S5] = TMP;\
+}
+
+static int reorder_self_2_4(void *src, unsigned int samples,
+ unsigned int samplesize, int chnum,
+ int s0, int s1, int s2, int s3, int s4, int s5)
+{
+ int i;
+ switch (samplesize) {
+ case 1:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2_4(src_8,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ } else {
+ REORDER_SELF_SWAP_2_4(src_8,tmp,samples,8,s0,s1,s2,s3,s4,s5);
+ }
+ break;
+ }
+ case 2:
+ {
+ int16_t *src_16 = src;
+ int16_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2_4(src_16,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ } else {
+ REORDER_SELF_SWAP_2_4(src_16,tmp,samples,8,s0,s1,s2,s3,s4,s5);
+ }
+ break;
+ }
+ case 3:
+ {
+ int8_t *src_8 = src;
+ int8_t tmp0, tmp1, tmp2;
+ for (i = 0; i < samples * 3; i += chnum * 3) {
+ tmp0 = src_8[i+s0*3];
+ tmp1 = src_8[i+s0*3+1];
+ tmp2 = src_8[i+s0*3+2];
+ src_8[i+s0*3] = src_8[i+s1*3];
+ src_8[i+s0*3+1] = src_8[i+s1*3+1];
+ src_8[i+s0*3+2] = src_8[i+s1*3+2];
+ src_8[i+s1*3] = tmp0;
+ src_8[i+s1*3+1] = tmp1;
+ src_8[i+s1*3+2] = tmp2;
+ tmp0 = src_8[i+s2*3];
+ tmp1 = src_8[i+s2*3+1];
+ tmp2 = src_8[i+s2*3+2];
+ src_8[i+s2*3] = src_8[i+s3*3];
+ src_8[i+s2*3+1] = src_8[i+s3*3+1];
+ src_8[i+s2*3+2] = src_8[i+s3*3+2];
+ src_8[i+s3*3] = src_8[i+s4*3];
+ src_8[i+s3*3+1] = src_8[i+s4*3+1];
+ src_8[i+s3*3+2] = src_8[i+s4*3+2];
+ src_8[i+s4*3] = src_8[i+s5*3];
+ src_8[i+s4*3+1] = src_8[i+s5*3+1];
+ src_8[i+s4*3+2] = src_8[i+s5*3+2];
+ src_8[i+s5*3] = tmp0;
+ src_8[i+s5*3+1] = tmp1;
+ src_8[i+s5*3+2] = tmp2;
+ }
+ break;
+ }
+ case 4:
+ {
+ int32_t *src_32 = src;
+ int32_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2_4(src_32,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ } else {
+ REORDER_SELF_SWAP_2_4(src_32,tmp,samples,8,s0,s1,s2,s3,s4,s5);
+ }
+ break;
+ }
+ case 8:
+ {
+ int64_t *src_64 = src;
+ int64_t tmp;
+ if (chnum==6) {
+ REORDER_SELF_SWAP_2_4(src_64,tmp,samples,6,s0,s1,s2,s3,s4,s5);
+ } else {
+ REORDER_SELF_SWAP_2_4(src_64,tmp,samples,8,s0,s1,s2,s3,s4,s5);
+ }
+ break;
+ }
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_ch] Unsupported sample size: %d, please "
+ "report this error on the MPlayer mailing list.\n",samplesize);
+ return 0;
+ }
+ return 1;
+}
+
+void reorder_channel(void *src,
+ int src_layout,
+ int dest_layout,
+ int samples,
+ int samplesize)
+{
+ if (dest_layout==src_layout)
+ return;
+ if (!AF_IS_SAME_CH_NUM(dest_layout,src_layout)) {
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_channel] different channel count "
+ "between current and target: %x, %x\n",
+ AF_GET_CH_NUM_WITH_LFE(src_layout),
+ AF_GET_CH_NUM_WITH_LFE(dest_layout));
+ return;
+ }
+ switch ((src_layout<<16)|dest_layout) {
+ // AF_CHANNEL_LAYOUT_5_0_A L R C Ls Rs
+ // AF_CHANNEL_LAYOUT_5_0_B L R Ls Rs C
+ // AF_CHANNEL_LAYOUT_5_0_C L C R Ls Rs
+ // AF_CHANNEL_LAYOUT_5_0_D C L R Ls Rs
+ case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_B:
+ reorder_self_3(src, samples, samplesize, 5, 2, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_C:
+ reorder_self_2(src, samples, samplesize, 5, 1, 2);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_D:
+ reorder_self_3(src, samples, samplesize, 5, 2, 1, 0);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_A:
+ reorder_self_3(src, samples, samplesize, 5, 4, 3, 2);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_C:
+ reorder_self_4_step_1(src, samples, samplesize, 5, 4, 3, 2, 1);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_D:
+ reorder_self_5_step_1(src, samples, samplesize, 5, 4, 3, 2, 1, 0);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_A:
+ reorder_self_2(src, samples, samplesize, 5, 1, 2);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_B:
+ reorder_self_4_step_1(src, samples, samplesize, 5, 1, 2, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_D:
+ reorder_self_2(src, samples, samplesize, 5, 0, 1);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_A:
+ reorder_self_3(src, samples, samplesize, 5, 0, 1, 2);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_B:
+ reorder_self_5_step_1(src, samples, samplesize, 5, 0, 1, 2, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_C:
+ reorder_self_2(src, samples, samplesize, 5, 0, 1);
+ break;
+ // AF_CHANNEL_LAYOUT_5_1_A L R C LFE Ls Rs
+ // AF_CHANNEL_LAYOUT_5_1_B L R Ls Rs C LFE
+ // AF_CHANNEL_LAYOUT_5_1_C L C R Ls Rs LFE
+ // AF_CHANNEL_LAYOUT_5_1_D C L R Ls Rs LFE
+ // AF_CHANNEL_LAYOUT_5_1_E LFE L C R Ls Rs
+ case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ if (samplesize != 3)
+ reorder_self_2(src, samples/2, samplesize*2, 3, 1, 2);
+ else
+ reorder_self_4_step_2(src, samples, samplesize, 6, 2, 3, 4, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_C:
+ reorder_self_2_3(src, samples, samplesize, 1, 2, 3, 4, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_D:
+ reorder_self_3_3(src, samples, samplesize, 2, 1, 0, 3, 4, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_A:
+ if (samplesize != 3)
+ reorder_self_2(src, samples/2, samplesize*2, 3, 1, 2);
+ else
+ reorder_self_4_step_2(src, samples, samplesize, 6, 2, 3, 4, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_C:
+ reorder_self_4_step_1(src, samples, samplesize, 6, 4, 3, 2, 1);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_D:
+ reorder_self_5_step_1(src, samples, samplesize, 6, 4, 3, 2, 1, 0);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_E:
+ reorder_self_2_4(src, samples, samplesize, 6, 2, 4, 5, 3, 1, 0);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_A:
+ reorder_self_2_3(src, samples, samplesize, 1, 2, 5, 4, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_self_4_step_1(src, samples, samplesize, 6, 1, 2, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_D:
+ reorder_self_2(src, samples, samplesize, 6, 0, 1);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_A:
+ reorder_self_3_3(src, samples, samplesize, 0, 1, 2, 5, 4, 3);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_self_5_step_1(src, samples, samplesize, 6, 0, 1, 2, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_C:
+ reorder_self_2(src, samples, samplesize, 6, 0, 1);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_E << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_self_2_4(src, samples, samplesize, 6, 2, 4, 0, 1, 3, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_5_1_F << 16 | AF_CHANNEL_LAYOUT_5_1_B:
+ reorder_self_2_4(src, samples, samplesize, 6, 3, 5, 0, 1, 2, 4);
+ break;
+ // AF_CHANNEL_LAYOUT_7_1_A L R C LFE Ls Rs Rls Rrs
+ // AF_CHANNEL_LAYOUT_7_1_B L R Ls Rs C LFE Rls Rrs
+ // AF_CHANNEL_LAYOUT_7_1_C L C R Ls Rs LFE Rls Rrs
+ // AF_CHANNEL_LAYOUT_7_1_D C L R Ls Rs Rls Rrs LFE
+ // AF_CHANNEL_LAYOUT_7_1_F C L R LFE Ls Rs Rls Rrs
+ case AF_CHANNEL_LAYOUT_7_1_A << 16 | AF_CHANNEL_LAYOUT_7_1_B:
+ case AF_CHANNEL_LAYOUT_7_1_B << 16 | AF_CHANNEL_LAYOUT_7_1_A:
+ if (samplesize != 3)
+ reorder_self_2(src, samples/2, samplesize*2, 4, 1, 2);
+ else
+ reorder_self_4_step_2(src, samples, samplesize, 8, 2, 3, 4, 5);
+ break;
+ case AF_CHANNEL_LAYOUT_7_1_B << 16 | AF_CHANNEL_LAYOUT_7_1_D:
+ // First convert to AF_CHANNEL_LAYOUT_7_1_F
+ reorder_self_2_4(src, samples, samplesize, 8, 3, 5, 4, 2, 1, 0);
+ // then convert to AF_CHANNEL_LAYOUT_7_1_D
+ reorder_self_5_step_1(src, samples, samplesize, 8, 3, 4, 5, 6, 7);
+ break;
+ case AF_CHANNEL_LAYOUT_7_1_C << 16 | AF_CHANNEL_LAYOUT_7_1_B:
+ reorder_self_4_step_1(src, samples, samplesize, 8, 1, 2, 3, 4);
+ break;
+ case AF_CHANNEL_LAYOUT_7_1_F << 16 | AF_CHANNEL_LAYOUT_7_1_B:
+ reorder_self_2_4(src, samples, samplesize, 8, 3, 5, 0, 1, 2, 4);
+ break;
+ default:
+ mp_msg(MSGT_GLOBAL, MSGL_WARN,
+ "[reorder_channel] unsupported from %x to %x, %d * %d\n",
+ src_layout, dest_layout, samples, samplesize);
+ }
+}
+
+
+static int channel_layout_mapping_5ch[AF_CHANNEL_LAYOUT_SOURCE_NUM] = {
+ AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_AAC_5CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_WAVEEX_5CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_LAVC_5CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_VORBIS_5CH_DEFAULT,
+};
+
+static int channel_layout_mapping_6ch[AF_CHANNEL_LAYOUT_SOURCE_NUM] = {
+ AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_AAC_6CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_WAVEEX_6CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_LAVC_6CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_VORBIS_6CH_DEFAULT,
+};
+
+static int channel_layout_mapping_8ch[AF_CHANNEL_LAYOUT_SOURCE_NUM] = {
+ AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_AAC_8CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_WAVEEX_8CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_LAVC_8CH_DEFAULT,
+ AF_CHANNEL_LAYOUT_VORBIS_8CH_DEFAULT,
+};
+
+void reorder_channel_copy_nch(void *src,
+ int src_layout,
+ void *dest,
+ int dest_layout,
+ int chnum,
+ int samples,
+ int samplesize)
+{
+ if (chnum < 5 || chnum == 7 || chnum > 8 ||
+ src_layout < 0 || dest_layout < 0 ||
+ src_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM ||
+ dest_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM)
+ memcpy(dest, src, samples*samplesize);
+ else if (chnum == 6)
+ reorder_channel_copy(src, channel_layout_mapping_6ch[src_layout],
+ dest, channel_layout_mapping_6ch[dest_layout],
+ samples, samplesize);
+ else if (chnum == 8)
+ reorder_channel_copy(src, channel_layout_mapping_8ch[src_layout],
+ dest, channel_layout_mapping_8ch[dest_layout],
+ samples, samplesize);
+ else
+ reorder_channel_copy(src, channel_layout_mapping_5ch[src_layout],
+ dest, channel_layout_mapping_5ch[dest_layout],
+ samples, samplesize);
+}
+
+void reorder_channel_nch(void *buf,
+ int src_layout,
+ int dest_layout,
+ int chnum,
+ int samples,
+ int samplesize)
+{
+ if (src_layout == dest_layout || chnum < 5 || chnum == 7 || chnum > 8 ||
+ src_layout < 0 || dest_layout < 0 ||
+ src_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM ||
+ dest_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM ||
+ src_layout == dest_layout)
+ return;
+ if (chnum == 6)
+ reorder_channel(buf, channel_layout_mapping_6ch[src_layout],
+ channel_layout_mapping_6ch[dest_layout],
+ samples, samplesize);
+ else if (chnum == 8)
+ reorder_channel(buf, channel_layout_mapping_8ch[src_layout],
+ channel_layout_mapping_8ch[dest_layout],
+ samples, samplesize);
+ else
+ reorder_channel(buf, channel_layout_mapping_5ch[src_layout],
+ channel_layout_mapping_5ch[dest_layout],
+ samples, samplesize);
+}
+
+
+#ifdef TEST
+
+static void test_copy(int channels) {
+ int samples = 12*1024*1024;
+ int samplesize = 2;
+ int i;
+ unsigned char *bufin = malloc((samples+100)*samplesize);
+ unsigned char *bufout = malloc((samples+100)*samplesize);
+ memset(bufin, 0xFF, samples*samplesize);
+ for (i = 0;i < 100; ++i)
+ reorder_channel_copy(bufin, AF_CHANNEL_LAYOUT_5_1_A,
+ bufout, AF_CHANNEL_LAYOUT_5_1_B,
+ samples, samplesize);
+// reorder_channel(bufin, AF_CHANNEL_LAYOUT_5_1_B,
+// AF_CHANNEL_LAYOUT_5_1_D,
+// samples, samplesize);
+ free(bufin);
+ free(bufout);
+}
+
+int main(int argc, char *argv[]) {
+ int channels = 6;
+ if (argc > 1)
+ channels = atoi(argv[1]);
+ test_copy(channels);
+ return 0;
+}
+
+#endif
diff --git a/audio/reorder_ch.h b/audio/reorder_ch.h
new file mode 100644
index 0000000000..44b533988d
--- /dev/null
+++ b/audio/reorder_ch.h
@@ -0,0 +1,133 @@
+/*
+ * common functions for reordering audio channels
+ *
+ * Copyright (C) 2007 Ulion <ulion A gmail P com>
+ *
+ * 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.
+ */
+
+#ifndef MPLAYER_REORDER_CH_H
+#define MPLAYER_REORDER_CH_H
+
+// L - Left
+// R - Right
+// C - Center
+// Ls - Left Surround
+// Rs - Right Surround
+// Cs - Center Surround
+// Rls - Rear Left Surround
+// Rrs - Rear Right Surround
+
+#define AF_LFE (1<<7)
+
+#define AF_CHANNEL_LAYOUT_MONO ((100<<8)|1)
+#define AF_CHANNEL_LAYOUT_STEREO ((101<<8)|2)
+#define AF_CHANNEL_LAYOUT_1_0 AF_CHANNEL_LAYOUT_MONO // C
+#define AF_CHANNEL_LAYOUT_2_0 AF_CHANNEL_LAYOUT_STEREO // L R
+#define AF_CHANNEL_LAYOUT_2_1 ((102<<8)|3) // L R LFE
+#define AF_CHANNEL_LAYOUT_3_0_A ((103<<8)|3) // L R C
+#define AF_CHANNEL_LAYOUT_3_0_B ((104<<8)|3) // C L R
+#define AF_CHANNEL_LAYOUT_4_0_A ((105<<8)|4) // L R C Cs
+#define AF_CHANNEL_LAYOUT_4_0_B ((106<<8)|4) // C L R Cs
+#define AF_CHANNEL_LAYOUT_4_0_C ((107<<8)|4) // L R Ls Rs
+#define AF_CHANNEL_LAYOUT_5_0_A ((108<<8)|5) // L R C Ls Rs
+#define AF_CHANNEL_LAYOUT_5_0_B ((109<<8)|5) // L R Ls Rs C
+#define AF_CHANNEL_LAYOUT_5_0_C ((110<<8)|5) // L C R Ls Rs
+#define AF_CHANNEL_LAYOUT_5_0_D ((111<<8)|5) // C L R Ls Rs
+#define AF_CHANNEL_LAYOUT_5_1_A ((112<<8)|6|AF_LFE) // L R C LFE Ls Rs
+#define AF_CHANNEL_LAYOUT_5_1_B ((113<<8)|6|AF_LFE) // L R Ls Rs C LFE
+#define AF_CHANNEL_LAYOUT_5_1_C ((114<<8)|6|AF_LFE) // L C R Ls Rs LFE
+#define AF_CHANNEL_LAYOUT_5_1_D ((115<<8)|6|AF_LFE) // C L R Ls Rs LFE
+#define AF_CHANNEL_LAYOUT_5_1_E ((116<<8)|6|AF_LFE) // LFE L C R Ls Rs
+#define AF_CHANNEL_LAYOUT_5_1_F ((117<<8)|6|AF_LFE) // C L R LFE Ls Rs
+#define AF_CHANNEL_LAYOUT_6_1_A ((118<<8)|7|AF_LFE) // L R C LFE Ls Rs Cs
+#define AF_CHANNEL_LAYOUT_7_1_A ((119<<8)|8|AF_LFE) // L R C LFE Ls Rs Rls Rrs
+#define AF_CHANNEL_LAYOUT_7_1_B ((120<<8)|8|AF_LFE) // L R Ls Rs C LFE Rls Rrs
+#define AF_CHANNEL_LAYOUT_7_1_C ((121<<8)|8|AF_LFE) // L C R Ls Rs LFE Rls Rrs
+#define AF_CHANNEL_LAYOUT_7_1_D ((122<<8)|8|AF_LFE) // C L R Ls Rs Rls Rrs LFE
+#define AF_CHANNEL_LAYOUT_7_1_F ((123<<8)|8|AF_LFE) // C L R LFE Ls Rs Rls Rrs
+
+
+#define AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_B
+#define AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_B
+#define AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_B
+#define AF_CHANNEL_LAYOUT_MPLAYER_5CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT
+#define AF_CHANNEL_LAYOUT_MPLAYER_6CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT
+#define AF_CHANNEL_LAYOUT_MPLAYER_8CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT
+#define AF_CHANNEL_LAYOUT_AAC_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_D
+#define AF_CHANNEL_LAYOUT_AAC_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_D
+#define AF_CHANNEL_LAYOUT_AAC_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_D
+#define AF_CHANNEL_LAYOUT_WAVEEX_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_A
+#define AF_CHANNEL_LAYOUT_WAVEEX_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_A
+#define AF_CHANNEL_LAYOUT_WAVEEX_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_A
+#define AF_CHANNEL_LAYOUT_LAVC_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_A
+#define AF_CHANNEL_LAYOUT_LAVC_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_A
+#define AF_CHANNEL_LAYOUT_LAVC_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_A
+#define AF_CHANNEL_LAYOUT_VORBIS_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_C
+#define AF_CHANNEL_LAYOUT_VORBIS_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_C
+#define AF_CHANNEL_LAYOUT_VORBIS_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_C
+
+#define AF_CHANNEL_MASK 0xFF
+#define AF_GET_CH_NUM(A) ((A)&0x7F)
+#define AF_GET_CH_NUM_WITH_LFE(A) ((A)&0xFF)
+#define AF_IS_SAME_CH_NUM(A,B) (((A)&0xFF)==((B)&0xFF))
+#define AF_IS_LAYOUT_SPECIFIED(A) ((A)&0xFFFFF800)
+#define AF_IS_LAYOUT_UNSPECIFIED(A) (!AF_IS_LAYOUT_SPECIFIED(A))
+
+/// Optimized channel reorder between channel layouts with same channel number.
+void reorder_channel_copy(void *src,
+ int src_layout,
+ void *dest,
+ int dest_layout,
+ int samples,
+ int samplesize);
+
+/// Same with reorder_channel_copy, but works on single buffer.
+void reorder_channel(void *buf,
+ int src_layout,
+ int dest_layout,
+ int samples,
+ int samplesize);
+
+// Channel layout definitions for different audio sources or targets
+// When specified channel number, they will be map to the specific layouts.
+#define AF_CHANNEL_LAYOUT_ALSA_DEFAULT 0
+#define AF_CHANNEL_LAYOUT_AAC_DEFAULT 1
+#define AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT 2
+#define AF_CHANNEL_LAYOUT_LAVC_DEFAULT 3
+#define AF_CHANNEL_LAYOUT_VORBIS_DEFAULT 4
+#define AF_CHANNEL_LAYOUT_SOURCE_NUM 5
+#define AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT AF_CHANNEL_LAYOUT_ALSA_DEFAULT
+
+/// Optimized channel reorder between different audio sources and targets.
+void reorder_channel_copy_nch(void *src,
+ int src_layout,
+ void *dest,
+ int dest_layout,
+ int chnum,
+ int samples,
+ int samplesize);
+
+/// Same with reorder_channel_copy_nch, but works on single buffer.
+void reorder_channel_nch(void *buf,
+ int src_layout,
+ int dest_layout,
+ int chnum,
+ int samples,
+ int samplesize);
+
+#endif /* MPLAYER_REORDER_CH_H */