]> git.alsa-project.org Git - alsa-tools.git/commitdiff
More enhancements:
authorJaroslav Kysela <perex@perex.cz>
Sun, 22 Apr 2001 12:04:00 +0000 (12:04 +0000)
committerJaroslav Kysela <perex@perex.cz>
Sun, 22 Apr 2001 12:04:00 +0000 (12:04 +0000)
  - 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

13 files changed:
ac3dec/Makefile.am
ac3dec/ac3dec.c
ac3dec/ac3spdif.c [new file with mode: 0644]
ac3dec/libac3/ac3.h
ac3dec/libac3/ac3_internal.h
ac3dec/libac3/bitstream.c
ac3dec/libac3/bitstream.h
ac3dec/libac3/decode.c
ac3dec/libac3/parse.c
ac3dec/libac3/parse.h
ac3dec/libac3/stats.c
ac3dec/output.c
ac3dec/output.h

index f215ef45f4d2ef6e646b49661beaafceb70e89a8..a3d1560d1139300d12389898e798b49da3a386ed 100644 (file)
@@ -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
 
 
index 81111b261d02dae2c6b823c9569df6da5621feff..15f7ec3384ad1398770494845340b3da5af8f7d7 100644 (file)
 #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;
+       *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;
 }
diff --git a/ac3dec/ac3spdif.c b/ac3dec/ac3spdif.c
new file mode 100644 (file)
index 0000000..04fd579
--- /dev/null
@@ -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;
+}
index 30d71163122a5c48f1c990affdf64b59562d67f8..d0ba1b7234bcd6a290703ec97e1b0ce934061da6 100644 (file)
@@ -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
index 8de184a968001134581b0c42fe4a573718fba833..7a20c5b7f0667b85cc23aeebedf7d18109d2f317 100644 (file)
@@ -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 */
index fdaeaf749ea7bd13d4db15a053e4a0ed21dea7d6..4d0b3ce0c84392a4f6fbbf1f50888b2faa2ef88c 100644 (file)
@@ -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;
index b2a148cdce254fc4af4df3501837f2655d3d12e1..7602d8acbda6e33758f01f9807c47938b5b9c9c2 100644 (file)
@@ -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);
 
index f0d90d2e8f8c85245126d5af9c42ee151f53da6b..2b09e56fafa16b927dfe8594ec1c093933bfb8ac 100644 (file)
@@ -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;
index 86e031c154fc2c0193b7e0fe0a3a6503e42f8cfe..0b16ccb8dd0512efe62ee77ac9904a86350ef1ce 100644 (file)
@@ -85,36 +85,11 @@ static const struct frmsize_s frmsizecod_tbl[64] =
        { 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)
        {
@@ -130,13 +105,44 @@ parse_syncinfo(syncinfo_t *syncinfo)
                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);
@@ -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,7 +162,8 @@ parse_syncinfo(syncinfo_t *syncinfo)
                return;
        }
 
-       stats_print_syncinfo(syncinfo);
+       if (!(ac3_config.flags & AC3_QUIET))
+               stats_print_syncinfo(syncinfo);
 }
 
 /*
@@ -290,7 +297,8 @@ parse_bsi(bsi_t *bsi)
                        bsi->addbsi[i] = bitstream_get(8);
        }
 
-       stats_print_bsi(bsi);
+       if (!(ac3_config.flags & AC3_QUIET))
+               stats_print_bsi(bsi);
 }
 
 /* More pain inducing parsing */
@@ -598,7 +606,8 @@ parse_audblk(bsi_t *bsi,audblk_t *audblk)
                }
        }
 
-       stats_print_audblk(bsi,audblk);
+       if (!(ac3_config.flags & AC3_QUIET))
+               stats_print_audblk(bsi,audblk);
 }
 
 void
index 72aa32c00f08b312ea37520f3080b07664de9e90..444932681e2b0e228673934961dd5db50e78e8c9 100644 (file)
@@ -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);
index 2ac88e0f5292d19bf88598183a570e83bc0acaad..d8a8417ec1275330aadb227f26dd6cdf718d6f3d 100644 (file)
@@ -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);
index e48c762fdbe97817735c24a77f82e0da3085eab9..c905582c913a6191a68d2284d3008638a798c822 100644 (file)
@@ -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,33 +46,36 @@ int output_open(int bits, int rate, int channels)
        snd_pcm_hw_params_alloca(&params);
        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;
        }
@@ -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 = 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;
@@ -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;
 }
 
 
index cb5a6aa19445c82ee3e95e9c37ec283fbf537dcf..1600ed21477189667636d871aa83029c7a2e3f4d 100644 (file)
  *
  */
 
-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);