mirror of
https://github.com/alsa-project/alsa-tools.git
synced 2025-11-01 22:58:48 -04:00
More enhancements:
- added getopt_long to parse the command line options - added -D (--device) option - added -q (--quiet) option - added -I (--iec958) option - added AC3 to IEC958 encapsulation code (not tested) - addec xrun detection and restart
This commit is contained in:
parent
3f541f6ba8
commit
97a55787e6
13 changed files with 425 additions and 120 deletions
|
|
@ -8,7 +8,7 @@ bin_PROGRAMS = ac3dec
|
|||
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
|
||||
|
||||
|
||||
|
|
|
|||
159
ac3dec/ac3dec.c
159
ac3dec/ac3dec.c
|
|
@ -26,17 +26,39 @@
|
|||
#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;
|
||||
|
||||
|
|
@ -44,56 +66,125 @@ void fill_buffer(uint_8 **start,uint_8 **end)
|
|||
|
||||
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;
|
||||
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;
|
||||
|
||||
morehelp = 0;
|
||||
while (1) {
|
||||
int c;
|
||||
|
||||
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 (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();
|
||||
output_open(16,ac3_frame->sampling_rate,channels);
|
||||
|
||||
do
|
||||
{
|
||||
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;
|
||||
}
|
||||
while((ac3_frame = ac3_decode_frame()));
|
||||
|
||||
output_close();
|
||||
fclose(in_file);
|
||||
return 0;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
137
ac3dec/ac3spdif.c
Normal file
137
ac3dec/ac3spdif.c
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/***************************************************************************/
|
||||
/* 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;
|
||||
}
|
||||
|
|
@ -39,13 +39,14 @@ typedef signed char sint_8;
|
|||
#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
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ typedef struct syncinfo_s
|
|||
{
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ static uint_8 *chunk_start, *chunk_end;
|
|||
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)
|
||||
{
|
||||
|
|
@ -122,7 +122,7 @@ bitstream_get_bh(uint_32 num_bits)
|
|||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ ac3_decode_frame(void)
|
|||
|
||||
parse_bsi(&bsi);
|
||||
|
||||
if(!done_banner)
|
||||
if(!done_banner && !(ac3_config.flags & AC3_QUIET))
|
||||
{
|
||||
stats_print_banner(&syncinfo,&bsi);
|
||||
done_banner = 1;
|
||||
|
|
|
|||
|
|
@ -85,14 +85,43 @@ static const struct frmsize_s frmsizecod_tbl[64] =
|
|||
{ 640 ,{1280 ,1394 ,1920 } }
|
||||
};
|
||||
|
||||
void
|
||||
parse_syncinfo_data(syncinfo_t *syncinfo, uint_8 *data)
|
||||
{
|
||||
// Get the sampling rate
|
||||
syncinfo->fscod = (data[2] >> 6) & 0x3;
|
||||
|
||||
if(syncinfo->fscod == 3)
|
||||
{
|
||||
//invalid sampling rate code
|
||||
error_flag = 1;
|
||||
return;
|
||||
}
|
||||
else if(syncinfo->fscod == 2)
|
||||
syncinfo->sampling_rate = 32000;
|
||||
else if(syncinfo->fscod == 1)
|
||||
syncinfo->sampling_rate = 44100;
|
||||
else
|
||||
syncinfo->sampling_rate = 48000;
|
||||
|
||||
// Get the frame size code
|
||||
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_32 tmp = 0;
|
||||
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.
|
||||
|
|
@ -109,34 +138,11 @@ parse_syncinfo(syncinfo_t *syncinfo)
|
|||
// 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;
|
||||
|
||||
if(syncinfo->fscod == 3)
|
||||
{
|
||||
//invalid sampling rate code
|
||||
error_flag = 1;
|
||||
return;
|
||||
}
|
||||
else if(syncinfo->fscod == 2)
|
||||
syncinfo->sampling_rate = 32000;
|
||||
else if(syncinfo->fscod == 1)
|
||||
syncinfo->sampling_rate = 44100;
|
||||
else
|
||||
syncinfo->sampling_rate = 48000;
|
||||
|
||||
// Get the frame size code
|
||||
syncinfo->frmsizecod = tmp & 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;
|
||||
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);
|
||||
|
|
@ -144,9 +150,9 @@ parse_syncinfo(syncinfo_t *syncinfo)
|
|||
// 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())
|
||||
|
|
@ -156,6 +162,7 @@ parse_syncinfo(syncinfo_t *syncinfo)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!(ac3_config.flags & AC3_QUIET))
|
||||
stats_print_syncinfo(syncinfo);
|
||||
}
|
||||
|
||||
|
|
@ -290,6 +297,7 @@ parse_bsi(bsi_t *bsi)
|
|||
bsi->addbsi[i] = bitstream_get(8);
|
||||
}
|
||||
|
||||
if (!(ac3_config.flags & AC3_QUIET))
|
||||
stats_print_bsi(bsi);
|
||||
}
|
||||
|
||||
|
|
@ -598,6 +606,7 @@ parse_audblk(bsi_t *bsi,audblk_t *audblk)
|
|||
}
|
||||
}
|
||||
|
||||
if (!(ac3_config.flags & AC3_QUIET))
|
||||
stats_print_audblk(bsi,audblk);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ static const char *language[128] =
|
|||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -27,14 +27,15 @@ typedef unsigned int uint_32;
|
|||
|
||||
#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;
|
||||
|
|
@ -45,18 +46,19 @@ int output_open(int bits, int rate, int channels)
|
|||
snd_pcm_hw_params_alloca(¶ms);
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
pcm_channels = channels;
|
||||
out_config = *output;
|
||||
|
||||
/*
|
||||
* Open the device driver
|
||||
*/
|
||||
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 (channels) {
|
||||
switch (output->channels) {
|
||||
case 1:
|
||||
case 2:
|
||||
sprintf(devstr, "plug:%d,%d", card, dev);
|
||||
|
|
@ -68,10 +70,12 @@ int output_open(int bits, int rate, int channels)
|
|||
sprintf(devstr, "surround51:%d,%d", card, dev);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%d channels are not supported\n", channels);
|
||||
fprintf(stderr, "%d channels are not supported\n", output->channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((err = snd_pcm_open(&pcm, devstr, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
||||
pcm_name = devstr;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
@ -87,17 +91,17 @@ int output_open(int bits, int rate, int channels)
|
|||
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;
|
||||
|
|
@ -108,12 +112,16 @@ int output_open(int bits, int rate, int channels)
|
|||
fprintf(stderr, "Buffer 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,
|
||||
100000, 0);
|
||||
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;
|
||||
|
|
@ -123,6 +131,50 @@ int output_open(int bits, int rate, int channels)
|
|||
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:
|
||||
|
|
@ -134,15 +186,20 @@ int output_open(int bits, int rate, int channels)
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,15 @@
|
|||
*
|
||||
*/
|
||||
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue