Commit 4f1d6d17 authored by Gildas Bazin's avatar Gildas Bazin

* libao/*: modified wav audio_out to output float multichannel wav files.

parent 8a640b55
......@@ -44,6 +44,7 @@ extern ao_open_t ao_windolby_open;
extern ao_open_t ao_wav_open;
extern ao_open_t ao_wavdolby_open;
extern ao_open_t ao_wav6_open;
extern ao_open_t ao_wavall_open;
extern ao_open_t ao_aif_open;
extern ao_open_t ao_aifdolby_open;
extern ao_open_t ao_peak_open;
......@@ -77,6 +78,7 @@ static ao_driver_t audio_out_drivers[] = {
{"wav", ao_wav_open},
{"wavdolby", ao_wavdolby_open},
{"wav6", ao_wav6_open},
{"wavall", ao_wavall_open},
{"aif", ao_aif_open},
{"aifdolby", ao_aifdolby_open},
{"peak", ao_peak_open},
......
......@@ -44,11 +44,16 @@ int channels_multi (int flags);
void convert2s16_multi (convert_t * f, int16_t * s16, int flags);
void convert2s16_wav (convert_t * f, int16_t * s16, int flags);
void s16_swap (int16_t * s16, int channels);
void s32_swap (int32_t * s32, int channels);
#ifdef WORDS_BIGENDIAN
#define s16_LE(s16,channels) s16_swap (s16, channels)
#define s16_BE(s16,channels) do {} while (0)
#define s32_LE(s32,channels) s32_swap (s32, channels)
#define s32_BE(s32,channels) do {} while (0)
#else
#define s16_LE(s16,channels) do {} while (0)
#define s16_BE(s16,channels) s16_swap (s16, channels)
#define s32_LE(s32,channels) do {} while (0)
#define s32_BE(s32,channels) s32_swap (s32, channels)
#endif
/*
* audio_out_wav.c
* Copyright (C) 2004 Gildas Bazin <gbazin@videolan.org>
* Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org>
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
*
* This file is part of a52dec, a free ATSC A-52 stream decoder.
* See http://liba52.sourceforge.net/ for updates.
* This file is part of dtsdec, a free DTS Coherent Acoustics stream decoder.
* See http://www.videolan.org/dtsdec.html for updates.
*
* a52dec is free software; you can redistribute it and/or modify
* dtsdec 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.
*
* a52dec is distributed in the hope that it will be useful,
* dtsdec 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.
......@@ -32,6 +33,30 @@
#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;
......@@ -44,32 +69,38 @@ typedef struct wav_instance_s {
static uint8_t wav_header[] = {
'R', 'I', 'F', 'F', 0xfc, 0xff, 0xff, 0xff, 'W', 'A', 'V', 'E',
'f', 'm', 't', ' ', 16, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 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 wav6_header[] = {
static uint8_t wavmulti_header[] = {
'R', 'I', 'F', 'F', 0xf0, 0xff, 0xff, 0xff, 'W', 'A', 'V', 'E',
'f', 'm', 't', ' ', 40, 0, 0, 0,
0xfe, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
22, 0, 16, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0x10, 0x00, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71,
(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)
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;
return 1;
instance->sample_rate = sample_rate;
if (instance->flags >= 0)
*flags = instance->flags;
*flags = instance->flags;
*level = CONVERT_LEVEL;
*bias = CONVERT_BIAS;
*bias = 0;
if( instance->flags == DTS_STEREO );
*bias = CONVERT_BIAS;
return 0;
}
......@@ -90,11 +121,25 @@ static void store2 (uint8_t * buf, int16_t value)
static int wav_channels (int flags, uint32_t * speaker_flags)
{
static const uint16_t speaker_tbl[] = {
3, 4, 3, 7, 0x103, 0x107, 0x33, 0x37, 4, 4, 3
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[] = {
2, 1, 2, 3, 3, 4, 4, 5, 1, 1, 2
static const uint8_t nfchans_tbl[] =
{
1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8
};
int chans;
......@@ -102,11 +147,11 @@ static int wav_channels (int flags, uint32_t * speaker_flags)
chans = nfchans_tbl[flags & DTS_CHANNEL_MASK];
if (flags & DTS_LFE) {
*speaker_flags |= 8; /* WAVE_SPEAKER_LOW_FREQUENCY */
chans++;
*speaker_flags |= SPEAKER_LOW_FREQUENCY;
chans++;
}
return chans;
return chans;
}
#include <stdio.h>
......@@ -114,48 +159,95 @@ static int wav_channels (int flags, uint32_t * speaker_flags)
static int wav_play (ao_instance_t * _instance, int flags, sample_t * _samples)
{
wav_instance_t * instance = (wav_instance_t *) _instance;
int16_t int16_samples[256 * 6];
int chans;
float ordered_samples[256 * 6];
int chans, size;
uint32_t speaker_flags;
static const int chan_map[10][6] =
{ { 0, 0, 0, 0, 0, 0 }, /* DTS_MONO */
{ 0, 1, 0, 0, 0, 0 }, /* DTS_CHANNEL */
{ 0, 1, 0, 0, 0, 0 }, /* DTS_STEREO */
{ 0, 1, 0, 0, 0, 0 }, /* DTS_STEREO_SUMDIFF */
{ 0, 1, 0, 0, 0, 0 }, /* DTS_STEREO_TOTAL */
{ 2, 0, 1, 0, 0, 0 }, /* DTS_3F */
{ 0, 1, 2, 0, 0, 0 }, /* DTS_2F1R */
{ 2, 0, 1, 3, 0, 0 }, /* DTS_3F1R */
{ 0, 1, 2, 3, 0, 0 }, /* DTS_2F2R */
{ 2, 0, 1, 3, 4, 0 }, /* DTS_3F2R */
};
static const int chan_map_lfe[10][6] =
{ { 0, 1, 0, 0, 0, 0 }, /* DTS_MONO */
{ 0, 1, 2, 0, 0, 0 }, /* DTS_CHANNEL */
{ 0, 1, 2, 0, 0, 0 }, /* DTS_STEREO */
{ 0, 1, 2, 0, 0, 0 }, /* DTS_STEREO_SUMDIFF */
{ 0, 1, 2, 0, 0, 0 }, /* DTS_STEREO_TOTAL */
{ 2, 0, 1, 3, 0, 0 }, /* DTS_3F */
{ 0, 1, 3, 2, 0, 0 }, /* DTS_2F1R */
{ 2, 0, 1, 4, 3, 0 }, /* DTS_3F1R */
{ 0, 1, 3, 4, 2, 0 }, /* DTS_2F2R */
{ 2, 0, 1, 4, 5, 3 }, /* DTS_3F2R */
};
#ifdef LIBDTS_DOUBLE
convert_t samples[256 * 6];
int i;
for (i = 0; i < 256 * 6; i++)
samples[i] = _samples[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 (speaker_flags == 3 || speaker_flags == 4) {
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 (wav6_header + 22, chans);
store4 (wav6_header + 24, instance->sample_rate);
store4 (wav6_header + 28, instance->sample_rate * 2 * chans);
store2 (wav6_header + 32, 2 * chans);
store4 (wav6_header + 40, speaker_flags);
fwrite (wav6_header, sizeof (wav6_header), 1, stdout);
}
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;
return 1;
convert2s16_wav (samples, int16_samples, flags);
s16_LE (int16_samples, chans);
fwrite (int16_samples, 256 * sizeof (int16_t) * chans, 1, stdout);
if (chans == 2) {
convert2s16_2 (samples, (int16_t *)ordered_samples);
s16_LE ((int16_t *)ordered_samples, chans);
size = 256 * sizeof (int16_t) * chans;
} else {
int i, j;
if (flags & DTS_LFE)
{
flags &= ~DTS_LFE;
for (j = 0; j < chans; j++)
for (i = 0; i < 256; i++)
ordered_samples[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[i * chans + chan_map[flags][j]] =
_samples[j * chans + i];
s32_LE (ordered_samples, chans);
size = 256 * sizeof (float) * chans;
}
instance->size += 256 * sizeof (int16_t) * chans;
fwrite (ordered_samples, size, 1, stdout);
instance->size += size;
return 0;
}
......@@ -165,16 +257,16 @@ static void wav_close (ao_instance_t * _instance)
wav_instance_t * instance = (wav_instance_t *) _instance;
if (fseek (stdout, 0, SEEK_SET) < 0)
return;
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);
store4 (wav_header + 4, instance->size + 36);
store4 (wav_header + 40, instance->size);
fwrite (wav_header, sizeof (wav_header), 1, stdout);
} else {
store4 (wav6_header + 4, instance->size + 60);
store4 (wav6_header + 64, instance->size);
fwrite (wav6_header, sizeof (wav6_header), 1, stdout);
store4 (wavmulti_header + 4, instance->size + 60);
store4 (wavmulti_header + 64, instance->size);
fwrite (wavmulti_header, sizeof (wavmulti_header), 1, stdout);
}
}
......@@ -184,7 +276,7 @@ static ao_instance_t * wav_open (int flags)
instance = (wav_instance_t *) malloc (sizeof (wav_instance_t));
if (instance == NULL)
return NULL;
return NULL;
instance->ao.setup = wav_setup;
instance->ao.play = wav_play;
......@@ -209,6 +301,11 @@ ao_instance_t * ao_wavdolby_open (void)
}
ao_instance_t * ao_wav6_open (void)
{
return wav_open (DTS_3F2R|DTS_LFE);
}
ao_instance_t * ao_wavall_open (void)
{
return wav_open (-1);
}
......@@ -192,116 +192,21 @@ void convert2s16_multi (convert_t * _f, int16_t * s16, int flags)
}
}
void convert2s16_wav (convert_t * _f, int16_t * s16, int flags)
void s16_swap (int16_t * s16, int channels)
{
int i;
int32_t * f = (int32_t *) _f;
uint16_t * u16 = (uint16_t *) s16;
switch (flags) {
case DTS_MONO:
convert2s16_1 (_f, s16);
break;
case DTS_CHANNEL:
case DTS_STEREO:
case DTS_DOLBY:
convert2s16_2 (_f, s16);
break;
case DTS_3F:
for (i = 0; i < 256; i++) {
s16[3*i] = convert (f[i]);
s16[3*i+1] = convert (f[i+512]);
s16[3*i+2] = convert (f[i+256]);
}
break;
case DTS_2F1R:
convert2s16_3 (_f, s16);
break;
case DTS_3F1R:
for (i = 0; i < 256; i++) {
s16[4*i] = convert (f[i]);
s16[4*i+1] = convert (f[i+512]);
s16[4*i+2] = convert (f[i+256]);
s16[4*i+3] = convert (f[i+768]);
}
break;
case DTS_2F2R:
convert2s16_4 (_f, s16);
break;
case DTS_3F2R:
for (i = 0; i < 256; i++) {
s16[5*i] = convert (f[i]);
s16[5*i+1] = convert (f[i+512]);
s16[5*i+2] = convert (f[i+256]);
s16[5*i+3] = convert (f[i+768]);
s16[5*i+4] = convert (f[i+1024]);
}
break;
case DTS_MONO | DTS_LFE:
for (i = 0; i < 256; i++) {
s16[2*i] = convert (f[i+256]);
s16[2*i+1] = convert (f[i]);
}
break;
case DTS_CHANNEL | DTS_LFE:
case DTS_STEREO | DTS_LFE:
case DTS_DOLBY | DTS_LFE:
for (i = 0; i < 256; i++) {
s16[3*i] = convert (f[i+256]);
s16[3*i+1] = convert (f[i+512]);
s16[3*i+2] = convert (f[i]);
}
break;
case DTS_3F | DTS_LFE:
for (i = 0; i < 256; i++) {
s16[4*i] = convert (f[i+256]);
s16[4*i+1] = convert (f[i+768]);
s16[4*i+2] = convert (f[i+512]);
s16[4*i+3] = convert (f[i]);
}
break;
case DTS_2F1R | DTS_LFE:
for (i = 0; i < 256; i++) {
s16[4*i] = convert (f[i+256]);
s16[4*i+1] = convert (f[i+512]);
s16[4*i+2] = convert (f[i]);
s16[4*i+3] = convert (f[i+768]);
}
case DTS_3F1R | DTS_LFE:
for (i = 0; i < 256; i++) {
s16[5*i] = convert (f[i+256]);
s16[5*i+1] = convert (f[i+768]);
s16[5*i+2] = convert (f[i+512]);
s16[5*i+3] = convert (f[i]);
s16[5*i+4] = convert (f[i+1024]);
}
break;
case DTS_2F2R | DTS_LFE:
for (i = 0; i < 256; i++) {
s16[5*i] = convert (f[i+256]);
s16[5*i+1] = convert (f[i+512]);
s16[5*i+2] = convert (f[i]);
s16[5*i+3] = convert (f[i+768]);
s16[5*i+4] = convert (f[i+1024]);
}
break;
case DTS_3F2R | DTS_LFE:
for (i = 0; i < 256; i++) {
s16[6*i] = convert (f[i+256]);
s16[6*i+1] = convert (f[i+768]);
s16[6*i+2] = convert (f[i+512]);
s16[6*i+3] = convert (f[i]);
s16[6*i+4] = convert (f[i+1024]);
s16[6*i+5] = convert (f[i+1280]);
}
break;
}
for (i = 0; i < 256 * channels; i++)
u16[i] = (u16[i] >> 8) | (u16[i] << 8);
}
void s16_swap (int16_t * s16, int channels)
void s32_swap (int32_t * s32, int channels)
{
int i;
uint16_t * u16 = (uint16_t *) s16;
uint32_t * u32 = (uint32_t *) s32;
for (i = 0; i < 256 * channels; i++)
u16[i] = (u16[i] >> 8) | (u16[i] << 8);
u32[i] = (u32[i] << 24) | ((u32[i] << 8)&0xFF0000) |
((u32[i] >> 8)&0xFF00) | (u32[i] >> 24);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment