ac3dec_LDADD= -L./libac3 -lac3
noinst_HEADERS = output.h
-ac3dec_SOURCES = ac3dec.c output.c
+ac3dec_SOURCES = ac3dec.c output.c ac3spdif.c
ac3dec_DEPENDENCIES = libac3/libac3.a
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include <sys/errno.h>
#include <errno.h>
+#include "config.h"
#include "libac3/ac3.h"
#include "output.h"
+void
+init_spdif(void);
+int
+output_spdif(uint_8 *data_start, uint_8 *data_end);
+
+static int quiet = 0;
+
+static void help(void)
+{
+ printf("Usage: ac3dec <options> [file] [[file]] ...\n");
+ printf("\nAvailable options:\n");
+ printf(" -h,--help this help\n");
+ printf(" -v,--version print version of this program\n");
+ printf(" -D,--device=NAME select PCM by NAME\n");
+ printf(" -4,--4ch four channels mode\n");
+ printf(" -6,--6ch six channels mode\n");
+ printf(" -I,--iec958 raw IEC958 (S/PDIF) mode\n");
+ printf(" -q,--quit quit mode\n");
+}
+
#define CHUNK_SIZE 2047
uint_8 buf[CHUNK_SIZE];
FILE *in_file;
-void fill_buffer(uint_8 **start,uint_8 **end)
+ssize_t fill_buffer(uint_8 **start,uint_8 **end)
{
uint_32 bytes_read;
bytes_read = fread(*start,1,CHUNK_SIZE,in_file);
- //FIXME hack...
+ if (feof(in_file))
+ return EOF;
if(bytes_read < CHUNK_SIZE)
- exit(0);
+ return EOF;
- *end= *start + bytes_read;
+ *end = *start + bytes_read;
+ return bytes_read;
}
int main(int argc,char *argv[])
{
- ac3_frame_t *ac3_frame;
+ struct option long_option[] =
+ {
+ {"help", 0, NULL, 'h'},
+ {"version", 0, NULL, 'v'},
+ {"device", 1, NULL, 'D'},
+ {"4ch", 0, NULL, '4'},
+ {"6ch", 0, NULL, '6'},
+ {"iec958", 0, NULL, 'I'},
+ {"spdif", 0, NULL, 'I'},
+ {"quit", 0, NULL, 'q'},
+ {NULL, 0, NULL, 0},
+ };
ac3_config_t ac3_config;
- int idx, channels = 2;
-
-
- /* If we get an argument then use it as a filename... otherwise use
- * stdin */
- idx = 1;
- if (idx < argc && !strcmp(argv[idx], "-4")) {
- channels = 4; idx++;
- } else if (idx < argc && !strcmp(argv[idx], "-6")) {
- channels = 6; idx++;
- }
- if (idx < argc && argv[idx] != NULL) {
- in_file = fopen(argv[idx],"r");
- if(!in_file)
- {
- fprintf(stderr,"%s - Couldn't open file %s\n",strerror(errno),argv[1]);
- exit(1);
- }
- }
- else
- in_file = stdin;
+ output_t out_config;
+ int morehelp, loop = 0;
+ char *pcm_name = NULL;
+ bzero(&ac3_config, sizeof(ac3_config));
ac3_config.fill_buffer_callback = fill_buffer;
- ac3_config.num_output_ch = channels;
+ ac3_config.num_output_ch = 2;
ac3_config.flags = 0;
+ bzero(&out_config, sizeof(out_config));
+ out_config.pcm_name = NULL;
+ out_config.bits = 16;
+ out_config.rate = 48000;
+ out_config.channels = 2;
+ out_config.spdif = 0;
- ac3_init(&ac3_config);
-
- ac3_frame = ac3_decode_frame();
- output_open(16,ac3_frame->sampling_rate,channels);
+ morehelp = 0;
+ while (1) {
+ int c;
- do
- {
- //Send the samples to the output device
- output_play(ac3_frame->audio_data, 256 * 6);
+ if ((c = getopt_long(argc, argv, "hvD:46Iq", long_option, NULL)) < 0)
+ break;
+ switch (c) {
+ case 'h':
+ morehelp++;
+ break;
+ case 'v':
+ printf("ac3dec version " VERSION "\n");
+ return 1;
+ case 'D':
+ pcm_name = optarg;
+ break;
+ case '4':
+ if (!out_config.spdif)
+ ac3_config.num_output_ch = 4;
+ break;
+ case '6':
+ if (!out_config.spdif)
+ ac3_config.num_output_ch = 6;
+ break;
+ case 'I':
+ ac3_config.num_output_ch = 2;
+ out_config.spdif = 1;
+ break;
+ case 'q':
+ ac3_config.flags |= AC3_QUIET;
+ out_config.quiet = 1;
+ quiet = 1;
+ break;
+ default:
+ fprintf(stderr, "\07Invalid switch or option needs an argument.\n");
+ morehelp++;
+ }
+ }
+ out_config.channels = ac3_config.num_output_ch;
+ if (morehelp) {
+ help();
+ return 1;
}
- while((ac3_frame = ac3_decode_frame()));
- output_close();
- fclose(in_file);
- return 0;
+ while (1) {
+ if (argc - optind <= 0) {
+ if (loop)
+ break;
+ in_file = stdin;
+ } else {
+ in_file = fopen(argv[optind],"r");
+ if(!in_file) {
+ fprintf(stderr,"%s - Couldn't open file %s\n",strerror(errno),argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ optind++;
+ loop++;
+ }
+ if (!out_config.spdif) {
+ ac3_frame_t *ac3_frame;
+ ac3_init(&ac3_config);
+ ac3_frame = ac3_decode_frame();
+ if (output_open(&out_config) < 0) {
+ fprintf(stderr, "Output open failed\n");
+ exit(EXIT_FAILURE);
+ }
+ do {
+ //Send the samples to the output device
+ output_play(ac3_frame->audio_data, 256 * 6);
+ } while((ac3_frame = ac3_decode_frame()));
+ } else {
+ uint_8 *start, *end;
+ init_spdif();
+ output_open(&out_config);
+ while (fill_buffer(&start, &end) > 0)
+ if (output_spdif(start, end) < 0)
+ break;
+ }
+ output_close();
+ fclose(in_file);
+ }
+ return EXIT_SUCCESS;
}
--- /dev/null
+/***************************************************************************/
+/* This code has been fully inspired from various place */
+/* I will give their name later. May they be bless .... It works */
+/* */
+/* For the moment test it. */
+/* */
+/* 27-08-00 -- Ze'ev Maor -- fixed recovery from flase syncword detection */
+/* */
+/* 24-08-00 -- Ze'ev Maor -- Modified for integrtion with DXR3-OMS-plugin */
+/***************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <libac3/ac3.h>
+#include <libac3/ac3_internal.h>
+#include <libac3/parse.h>
+#include <libac3/crc.h>
+
+#include "output.h"
+
+void swab(const void*, void*, size_t);
+
+#define BLOCK_SIZE 6144
+
+static char buf[BLOCK_SIZE];
+static uint32_t sbuffer_size;
+static syncinfo_t syncinfo;
+static char *sbuffer;
+static int done_banner;
+
+static uint32_t
+buffer_syncframe(syncinfo_t *syncinfo, uint8_t **start, uint8_t *end)
+{
+ uint8_t *cur = *start;
+ uint16_t syncword = syncinfo->syncword;
+ uint32_t ret = 0;
+
+ //
+ // Find an ac3 sync frame.
+ //
+ while(syncword != 0x0b77)
+ {
+ if(cur >= end)
+ goto done;
+ syncword = (syncword << 8) + *cur++;
+ }
+
+ //need the next 3 bytes to decide how big the frame is
+ while(sbuffer_size < 3)
+ {
+ if(cur >= end)
+ goto done;
+
+ sbuffer[sbuffer_size++] = *cur++;
+ }
+
+ parse_syncinfo_data(syncinfo,sbuffer);
+
+ while(sbuffer_size < syncinfo->frame_size * 2 - 2)
+ {
+ if(cur >= end)
+ goto done;
+
+ sbuffer[sbuffer_size++] = *cur++;
+ }
+
+ crc_init();
+ crc_process_frame(sbuffer,syncinfo->frame_size * 2 - 2);
+
+ if(!crc_validate())
+ {
+ //error_flag = 1;
+ fprintf(stderr,"** CRC failed - skipping frame **\n");
+ syncword = 0xffff;
+ sbuffer_size = 0;
+ bzero(buf,BLOCK_SIZE);
+
+ goto done;
+ }
+ //
+ //if we got to this point, we found a valid ac3 frame to decode
+ //
+
+ //reset the syncword for next time
+ syncword = 0xffff;
+ sbuffer_size = 0;
+ ret = 1;
+
+ done:
+ syncinfo->syncword = syncword;
+ *start = cur;
+ return ret;
+}
+
+void
+init_spdif(void)
+{
+ sbuffer_size = 0;
+ sbuffer = &buf[10];
+ done_banner = 0;
+}
+
+int
+output_spdif(uint8_t *data_start, uint8_t *data_end, int quiet)
+{
+ unsigned short *sbuf = (unsigned short *)buf;
+ int ret = 0, res;
+
+ while(buffer_syncframe(&syncinfo, &data_start, data_end))
+ {
+ sbuf[0] = 0xf872; //spdif syncword
+ sbuf[1] = 0x4e1f; // .............
+ sbuf[2] = 0x0001; // AC3 data
+ sbuf[3] = syncinfo.frame_size * 16;
+ sbuf[4] = 0x0b77; // AC3 syncwork
+
+ if (!done_banner) {
+ fprintf(stdout,"AC3 Stream ");
+ fprintf(stdout,"%2.1f KHz",syncinfo.sampling_rate * 1e-3);
+ fprintf(stdout,"%4d kbps",syncinfo.bit_rate);
+ fprintf(stdout,"\n");
+ done_banner = 1;
+ }
+
+ // extract_ac3 seems to write swabbed data
+ swab(&buf[10], &buf[10], syncinfo.frame_size * 2 - 2);
+ res = output_play(sbuf, BLOCK_SIZE / 2 / 2); /* 2 channels, 16-bit samples */
+ ret = ret < 0 ? ret : res;
+ bzero(buf,BLOCK_SIZE);
+ }
+ return ret;
+}
#define AC3_3DNOW_ENABLE 0x2
#define AC3_MMX_ENABLE 0x4
#define AC3_ALTIVEC_ENABLE 0x8
+#define AC3_QUIET 0x10
typedef struct ac3_config_s
{
//Bit flags that enable various things
uint_32 flags;
//Callback that points the decoder to new stream data
- void (*fill_buffer_callback)(uint_8 **, uint_8 **);
+ ssize_t (*fill_buffer_callback)(uint_8 **, uint_8 **);
//Number of discrete channels in final output (for downmixing)
uint_16 num_output_ch;
//Which channel of a dual mono stream to select
{
uint_32 magic;
/* Sync word == 0x0B77 */
- /* uint_16 syncword; */
+ uint_16 syncword;
/* crc for the first 5/8 of the sync block */
/* uint_16 crc1; */
/* Stream Sampling Rate (kHz) 0 = 48, 1 = 44.1, 2 = 32, 3 = reserved */
uint_32 bits_left;
uint_32 current_word;
-void (*bitstream_fill_buffer)(uint_8**,uint_8**);
+ssize_t (*bitstream_fill_buffer)(uint_8**,uint_8**);
uint_8 bitstream_get_byte(void)
{
}
void
-bitstream_init(void(*fill_function)(uint_8**,uint_8**))
+bitstream_init(ssize_t(*fill_function)(uint_8**,uint_8**))
{
// Setup the buffer fill callback
bitstream_fill_buffer = fill_function;
extern uint_32 bits_left;
extern uint_32 current_word;
-void bitstream_init(void(*fill_function)(uint_8**,uint_8**));
+void bitstream_init(ssize_t(*fill_function)(uint_8**,uint_8**));
uint_8 bitstream_get_byte(void);
parse_bsi(&bsi);
- if(!done_banner)
+ if(!done_banner && !(ac3_config.flags & AC3_QUIET))
{
stats_print_banner(&syncinfo,&bsi);
done_banner = 1;
{ 640 ,{1280 ,1394 ,1920 } }
};
-/* Parse a syncinfo structure, minus the sync word */
void
-parse_syncinfo(syncinfo_t *syncinfo)
+parse_syncinfo_data(syncinfo_t *syncinfo, uint_8 *data)
{
- uint_32 tmp = 0;
- uint_16 sync_word = 0;
- uint_32 time_out = 1<<16;
-
- //
- // Find a ac3 sync frame. Time out if we read 64k without finding
- // one.
- //
- while(time_out--)
- {
- sync_word = (sync_word << 8) + bitstream_get_byte();
-
- if(sync_word == 0x0b77)
- break;
- }
-
- //
- // We need to read in the entire syncinfo struct (0x0b77 + 24 bits)
- // in order to determine how big the frame is
- //
- tmp = (tmp << 8) + bitstream_get_byte();
- tmp = (tmp << 8) + bitstream_get_byte();
- tmp = (tmp << 8) + bitstream_get_byte();
-
// Get the sampling rate
- syncinfo->fscod = (tmp >> 6) & 0x3;
+ syncinfo->fscod = (data[2] >> 6) & 0x3;
if(syncinfo->fscod == 3)
{
syncinfo->sampling_rate = 48000;
// Get the frame size code
- syncinfo->frmsizecod = tmp & 0x3f;
+ syncinfo->frmsizecod = data[2] & 0x3f;
// Calculate the frame size and bitrate
syncinfo->frame_size =
frmsizecod_tbl[syncinfo->frmsizecod].frm_size[syncinfo->fscod];
syncinfo->bit_rate = frmsizecod_tbl[syncinfo->frmsizecod].bit_rate;
+}
+
+/* Parse a syncinfo structure, minus the sync word */
+void
+parse_syncinfo(syncinfo_t *syncinfo)
+{
+ uint_8 data[3];
+ uint_16 sync_word = 0;
+ uint_32 time_out = 1<<16;
+
+
+ //
+ // Find a ac3 sync frame. Time out if we read 64k without finding
+ // one.
+ //
+ while(time_out--)
+ {
+ sync_word = (sync_word << 8) + bitstream_get_byte();
+
+ if(sync_word == 0x0b77)
+ break;
+ }
+
+ //
+ // We need to read in the entire syncinfo struct (0x0b77 + 24 bits)
+ // in order to determine how big the frame is
+ //
+ data[0] = bitstream_get_byte();
+ data[1] = bitstream_get_byte();
+ data[2] = bitstream_get_byte();
+ parse_syncinfo_data(syncinfo, data);
// Buffer the entire syncframe
bitstream_buffer_frame(syncinfo->frame_size * 2 - 5);
// Check the crc over the entire frame
crc_init();
- crc_process_byte(tmp>>16);
- crc_process_byte((tmp>>8) & 0xff);
- crc_process_byte(tmp & 0xff);
+ crc_process_byte(data[0]);
+ crc_process_byte(data[1]);
+ crc_process_byte(data[2]);
crc_process_frame(bitstream_get_buffer_start(),syncinfo->frame_size * 2 - 5);
if(!crc_validate())
return;
}
- stats_print_syncinfo(syncinfo);
+ if (!(ac3_config.flags & AC3_QUIET))
+ stats_print_syncinfo(syncinfo);
}
/*
bsi->addbsi[i] = bitstream_get(8);
}
- stats_print_bsi(bsi);
+ if (!(ac3_config.flags & AC3_QUIET))
+ stats_print_bsi(bsi);
}
/* More pain inducing parsing */
}
}
- stats_print_audblk(bsi,audblk);
+ if (!(ac3_config.flags & AC3_QUIET))
+ stats_print_audblk(bsi,audblk);
}
void
*
*/
+void parse_syncinfo_data(syncinfo_t *syncinfo, uint_8 *data);
void parse_syncinfo(syncinfo_t *syncinfo);
void parse_audblk(bsi_t *bsi,audblk_t *audblk);
void parse_bsi(bsi_t *bsi);
void stats_print_banner(syncinfo_t *syncinfo,bsi_t *bsi)
{
- fprintf(stdout,PACKAGE"-"VERSION" (C) 2000 Aaron Holtzman (aholtzma@ess.engr.uvic.ca)\n");
+ // fprintf(stdout,PACKAGE"-"VERSION" (C) 2000 Aaron Holtzman (aholtzma@ess.engr.uvic.ca)\n");
fprintf(stdout,"%d.%d Mode ",bsi->nfchans,bsi->lfeon);
fprintf(stdout,"%2.1f KHz",syncinfo->sampling_rate * 1e-3);
#include "output.h"
-static int pcm_channels;
+static output_t out_config;
static snd_pcm_t *pcm;
/*
* open the audio device for writing to
*/
-int output_open(int bits, int rate, int channels)
+int output_open(output_t *output)
{
+ const char *pcm_name = output->pcm_name;
char devstr[128];
int card, dev;
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_sw_params_alloca(&swparams);
- pcm_channels = channels;
+ out_config = *output;
/*
* Open the device driver
*/
- card = snd_defaults_pcm_card();
- dev = snd_defaults_pcm_device();
- if (card < 0 || dev < 0) {
- fprintf(stderr, "defaults are not set\n");
- return -ENODEV;
+ if (pcm_name == NULL) {
+ card = snd_defaults_pcm_card();
+ dev = snd_defaults_pcm_device();
+ if (card < 0 || dev < 0) {
+ fprintf(stderr, "defaults are not set\n");
+ return -ENODEV;
+ }
+ switch (output->channels) {
+ case 1:
+ case 2:
+ sprintf(devstr, "plug:%d,%d", card, dev);
+ break;
+ case 4:
+ sprintf(devstr, "surround40:%d,%d", card, dev);
+ break;
+ case 6:
+ sprintf(devstr, "surround51:%d,%d", card, dev);
+ break;
+ default:
+ fprintf(stderr, "%d channels are not supported\n", output->channels);
+ return -EINVAL;
+ }
+ pcm_name = devstr;
}
- switch (channels) {
- case 1:
- case 2:
- sprintf(devstr, "plug:%d,%d", card, dev);
- break;
- case 4:
- sprintf(devstr, "surround40:%d,%d", card, dev);
- break;
- case 6:
- sprintf(devstr, "surround51:%d,%d", card, dev);
- break;
- default:
- fprintf(stderr, "%d channels are not supported\n", channels);
- return -EINVAL;
- }
- if ((err = snd_pcm_open(&pcm, devstr, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+ if ((err = snd_pcm_open(&pcm, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf(stderr, "snd_pcm_open: %s\n", snd_strerror(err));
return err;
}
fprintf(stderr, "Access type not available");
goto __close;
}
- err = snd_pcm_hw_params_set_format(pcm, params, bits == 16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8);
+ err = snd_pcm_hw_params_set_format(pcm, params, output->bits == 16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8);
if (err < 0) {
fprintf(stderr, "Sample format non available");
goto __close;
}
- err = snd_pcm_hw_params_set_channels(pcm, params, channels);
+ err = snd_pcm_hw_params_set_channels(pcm, params, output->channels);
if (err < 0) {
fprintf(stderr, "Channels count non available");
goto __close;
}
- err = snd_pcm_hw_params_set_rate_near(pcm, params, rate, 0);
+ err = snd_pcm_hw_params_set_rate_near(pcm, params, output->rate, 0);
if (err < 0) {
fprintf(stderr, "Rate not available");
goto __close;
fprintf(stderr, "Buffer time not available");
goto __close;
}
- period_time = snd_pcm_hw_params_set_period_time_near(pcm, params,
- 100000, 0);
- if (period_time < 0) {
- fprintf(stderr, "Period time not available");
- goto __close;
- }
+ period_time = 100000 * 2;
+ do {
+ period_time /= 2;
+ period_time = snd_pcm_hw_params_set_period_time_near(pcm, params,
+ period_time, 0);
+ if (period_time < 0) {
+ fprintf(stderr, "Period time not available");
+ goto __close;
+ }
+ } while (buffer_time == period_time && period_time > 10000);
if (buffer_time == period_time) {
fprintf(stderr, "Buffer time and period time match, could not use\n");
goto __close;
goto __close;
}
+ if (output->spdif) {
+ snd_pcm_info_t *info;
+ snd_ctl_elem_value_t *ctl;
+ snd_ctl_t *ctl_handle;
+ char ctl_name[12];
+ int ctl_card;
+ snd_aes_iec958_t spdif;
+
+ memset(&spdif, 0, sizeof(spdif));
+ spdif.status[0] = IEC958_AES0_NONAUDIO |
+ IEC958_AES0_CON_EMPHASIS_NONE;
+ spdif.status[1] = IEC958_AES1_CON_ORIGINAL |
+ IEC958_AES1_CON_PCM_CODER;
+ spdif.status[2] = 0;
+ spdif.status[3] = IEC958_AES3_CON_FS_48000;
+ snd_pcm_info_alloca(&info);
+ if ((err = snd_pcm_info(pcm, info)) < 0) {
+ fprintf(stderr, "pcm info error: %s", snd_strerror(err));
+ goto __close;
+ }
+ snd_ctl_elem_value_alloca(&ctl);
+ snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM);
+ snd_ctl_elem_value_set_device(ctl, snd_pcm_info_get_device(info));
+ snd_ctl_elem_value_set_subdevice(ctl, snd_pcm_info_get_subdevice(info));
+ snd_ctl_elem_value_set_name(ctl, SND_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM));
+ snd_ctl_elem_value_set_iec958(ctl, &spdif);
+ ctl_card = snd_pcm_info_get_card(info);
+ if (ctl_card < 0) {
+ fprintf(stderr, "Unable to setup the IEC958 (S/PDIF) interface - PCM has no assigned card\n");
+ goto __diga_end;
+ }
+ sprintf(ctl_name, "hw:%d", ctl_card);
+ if ((err = snd_ctl_open(&ctl_handle, ctl_name, 0)) < 0) {
+ fprintf(stderr, "Unable to open the control interface '%s': %s\n", ctl_name, snd_strerror(err));
+ goto __diga_end;
+ }
+ if ((err = snd_ctl_elem_write(ctl_handle, ctl)) < 0) {
+ fprintf(stderr, "Unable to update the IEC958 control: %s\n", snd_strerror(err));
+ goto __diga_end;
+ }
+ snd_ctl_close(ctl_handle);
+ __diga_end:
+ }
+
return 0;
__close:
/*
* play the sample to the already opened file descriptor
*/
-void output_play(sint_16* output_samples, uint_32 num_frames)
+int output_play(sint_16* output_samples, uint_32 num_frames)
{
- snd_pcm_sframes_t res;
+ snd_pcm_sframes_t res = 0;
- res = snd_pcm_writei(pcm, (void *)output_samples, num_frames);
+ do {
+ if (res == -EPIPE)
+ res = snd_pcm_prepare(pcm);
+ res = res < 0 ? res : snd_pcm_writei(pcm, (void *)output_samples, num_frames);
+ } while (res == -EPIPE);
if (res < 0)
fprintf(stderr, "writei returned error: %s\n", snd_strerror(res));
else if (res != num_frames)
fprintf(stderr, "writei retured %li (expected %li)\n", res, (long)(num_frames));
+ return res < 0 ? (int)res : 0;
}
*
*/
-int output_open(int bits, int rate, int channels);
-void output_play(sint_16* output_samples, uint_32 num_bytes);
+typedef struct {
+ const char *pcm_name;
+ int bits;
+ int rate;
+ int channels;
+ int spdif: 1;
+ int quiet: 1;
+} output_t;
+
+int output_open(output_t *output);
+int output_play(sint_16* output_samples, uint_32 num_bytes);
void output_close(void);