/* * audio_out_wav.c * Copyright (C) 2004 Gildas Bazin * Copyright (C) 2000-2003 Michel Lespinasse * Copyright (C) 1999-2000 Aaron Holtzman * * This file is part of libdca, a free DTS Coherent Acoustics stream decoder. * See http://www.videolan.org/developers/libdca.html for updates. * * libdca 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. * * libdca is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include "dca.h" #include "audio_out.h" #include "audio_out_internal.h" #define WAVE_FORMAT_PCM 0x0001 #define WAVE_FORMAT_IEEE_FLOAT 0x0003 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE #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 typedef struct wav_instance_s { ao_instance_t ao; int sample_rate; int set_params; int flags; uint32_t speaker_flags; int size; } wav_instance_t; static uint8_t wav_header[] = { 'R', 'I', 'F', 'F', 0xfc, 0xff, 0xff, 0xff, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', 16, 0, 0, 0, WAVE_FORMAT_PCM, WAVE_FORMAT_PCM >> 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 'd', 'a', 't', 'a', 0xd8, 0xff, 0xff, 0xff }; static uint8_t wavmulti_header[] = { 'R', 'I', 'F', 'F', 0xf0, 0xff, 0xff, 0xff, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', 40, 0, 0, 0, (uint8_t)(WAVE_FORMAT_EXTENSIBLE & 0xFF), WAVE_FORMAT_EXTENSIBLE >> 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 22, 0, 0, 0, 0, 0, 0, 0, WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT >> 8, 0, 0, 0, 0, 0x10, 0x00, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71, 'd', 'a', 't', 'a', 0xb4, 0xff, 0xff, 0xff }; static int wav_setup (ao_instance_t * _instance, int sample_rate, int * flags, level_t * level, sample_t * bias) { wav_instance_t * instance = (wav_instance_t *) _instance; if ((instance->set_params == 0) && (instance->sample_rate != sample_rate)) return 1; instance->sample_rate = sample_rate; if (instance->flags >= 0) *flags = instance->flags; *level = CONVERT_LEVEL; *bias = 0; if( instance->flags == DCA_STEREO ) *bias = CONVERT_BIAS; return 0; } static void store4 (uint8_t * buf, int value) { buf[0] = value; buf[1] = value >> 8; buf[2] = value >> 16; buf[3] = value >> 24; } static void store2 (uint8_t * buf, int16_t value) { buf[0] = value; buf[1] = value >> 8; } static int wav_channels (int flags, uint32_t * speaker_flags) { static const uint32_t speaker_tbl[] = { SPEAKER_FRONT_CENTER, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_CENTER, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, /* TODO > 5 channels */ }; static const uint8_t nfchans_tbl[] = { 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8 }; int chans; *speaker_flags = speaker_tbl[flags & DCA_CHANNEL_MASK]; chans = nfchans_tbl[flags & DCA_CHANNEL_MASK]; if (flags & DCA_LFE) { *speaker_flags |= SPEAKER_LOW_FREQUENCY; chans++; } return chans; } #include static int wav_play (ao_instance_t * _instance, int flags, sample_t * _samples) { wav_instance_t * instance = (wav_instance_t *) _instance; union { float floats[256 * 6]; int16_t words[0]; int32_t dwords[0]; } ordered_samples; int chans, size; uint32_t speaker_flags; static const int chan_map[10][6] = { { 0, 0, 0, 0, 0, 0 }, /* DCA_MONO */ { 0, 1, 0, 0, 0, 0 }, /* DCA_CHANNEL */ { 0, 1, 0, 0, 0, 0 }, /* DCA_STEREO */ { 0, 1, 0, 0, 0, 0 }, /* DCA_STEREO_SUMDIFF */ { 0, 1, 0, 0, 0, 0 }, /* DCA_STEREO_TOTAL */ { 2, 0, 1, 0, 0, 0 }, /* DCA_3F */ { 0, 1, 2, 0, 0, 0 }, /* DCA_2F1R */ { 2, 0, 1, 3, 0, 0 }, /* DCA_3F1R */ { 0, 1, 2, 3, 0, 0 }, /* DCA_2F2R */ { 2, 0, 1, 3, 4, 0 }, /* DCA_3F2R */ }; static const int chan_map_lfe[10][6] = { { 0, 1, 0, 0, 0, 0 }, /* DCA_MONO */ { 0, 1, 2, 0, 0, 0 }, /* DCA_CHANNEL */ { 0, 1, 2, 0, 0, 0 }, /* DCA_STEREO */ { 0, 1, 2, 0, 0, 0 }, /* DCA_STEREO_SUMDIFF */ { 0, 1, 2, 0, 0, 0 }, /* DCA_STEREO_TOTAL */ { 2, 0, 1, 3, 0, 0 }, /* DCA_3F */ { 0, 1, 3, 2, 0, 0 }, /* DCA_2F1R */ { 2, 0, 1, 4, 3, 0 }, /* DCA_3F1R */ { 0, 1, 3, 4, 2, 0 }, /* DCA_2F2R */ { 2, 0, 1, 4, 5, 3 }, /* DCA_3F2R */ }; #ifdef LIBDCA_DOUBLE convert_t samples[256 * 6]; int i; for (i = 0; i < 256 * 6; i++) samples[i] = _samples[i]; #else convert_t * samples = _samples; #endif chans = wav_channels (flags, &speaker_flags); if (instance->set_params) { instance->set_params = 0; instance->speaker_flags = speaker_flags; if (chans == 2) { store2 (wav_header + 22, chans); store4 (wav_header + 24, instance->sample_rate); store4 (wav_header + 28, instance->sample_rate * 2 * chans); store2 (wav_header + 32, 2 * chans); fwrite (wav_header, sizeof (wav_header), 1, stdout); } else { store2 (wavmulti_header + 22, chans); store4 (wavmulti_header + 24, instance->sample_rate); store4 (wavmulti_header + 28, instance->sample_rate * sizeof(float) * chans); store2 (wavmulti_header + 32, sizeof(float) * chans); store2 (wavmulti_header + 38, sizeof(float) * 8); store4 (wavmulti_header + 40, speaker_flags); fwrite (wavmulti_header, sizeof (wavmulti_header), 1, stdout); } } else if (speaker_flags != instance->speaker_flags) return 1; if (chans == 2) { convert2s16_2 (samples, ordered_samples.words); s16_LE (ordered_samples.words, chans); size = 256 * sizeof (int16_t) * chans; } else { int i, j; if (flags & DCA_LFE) { flags &= ~DCA_LFE; for (j = 0; j < chans; j++) for (i = 0; i < 256; i++) ordered_samples.floats[i * chans + chan_map_lfe[flags][j]] = _samples[j * 256 + i]; } else for (j = 0; j < chans; j++) for (i = 0; i < 256; i++) ordered_samples.floats[i * chans + chan_map[flags][j]] = _samples[j * chans + i]; s32_LE (ordered_samples.dwords, chans); size = 256 * sizeof (float) * chans; } fwrite (&ordered_samples, size, 1, stdout); instance->size += size; return 0; } static void wav_close (ao_instance_t * _instance) { wav_instance_t * instance = (wav_instance_t *) _instance; if (fseek (stdout, 0, SEEK_SET) < 0) return; if (instance->speaker_flags == 3 || instance->speaker_flags == 4) { store4 (wav_header + 4, instance->size + 36); store4 (wav_header + 40, instance->size); fwrite (wav_header, sizeof (wav_header), 1, stdout); } else { store4 (wavmulti_header + 4, instance->size + 60); store4 (wavmulti_header + 64, instance->size); fwrite (wavmulti_header, sizeof (wavmulti_header), 1, stdout); } } static ao_instance_t * wav_open (int flags) { wav_instance_t * instance; instance = (wav_instance_t *) malloc (sizeof (wav_instance_t)); if (instance == NULL) return NULL; instance->ao.setup = wav_setup; instance->ao.play = wav_play; instance->ao.close = wav_close; instance->sample_rate = 0; instance->set_params = 1; instance->flags = flags; instance->size = 0; return (ao_instance_t *) instance; } ao_instance_t * ao_wav_open (void) { return wav_open (DCA_STEREO); } ao_instance_t * ao_wavdolby_open (void) { return wav_open (DCA_DOLBY); } ao_instance_t * ao_wav6_open (void) { return wav_open (DCA_3F2R|DCA_LFE); } ao_instance_t * ao_wavall_open (void) { return wav_open (-1); }