]> git.sesse.net Git - vlc/blobdiff - modules/demux/voc.c
Removes trailing spaces. Removes tabs.
[vlc] / modules / demux / voc.c
index b51b64df202bbf72029d4988b206e4ea137d713f..af211f0a737ce09df9806205157b7e3f86092b5f 100644 (file)
@@ -1,10 +1,10 @@
 /*****************************************************************************
  * voc.c : Creative Voice File (.VOC) demux module for vlc
  *****************************************************************************
- * Copyright (C) 2005 VideoLAN
+ * Copyright (C) 2005 Rémi Denis-Courmont
  * $Id$
  *
- * Authors: Remi Denis-Courmont <rem # videolan.org>
+ * Authors: Rémi Denis-Courmont <rem # videolan.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
-#include <stdlib.h>                                      /* malloc(), free() */
 
 #include <vlc/vlc.h>
-#include <vlc/input.h>
-#include <vlc/aout.h>
+#include <vlc_demux.h>
+#include <vlc_aout.h>
 
-#include <codecs.h>
+#include <vlc_codecs.h>
 
 /*****************************************************************************
  * Module descriptor
@@ -57,14 +56,13 @@ struct demux_sys_t
     es_format_t     fmt;
     es_out_id_t     *p_es;
 
-    int64_t         i_block_offset;
+    int64_t         i_block_start;
+    int64_t         i_block_end;
 
     int64_t         i_loop_offset;
     unsigned        i_loop_count;
     unsigned        i_silence_countdown;
 
-    int32_t         i_block_size;    
-
     date_t          pts;
 };
 
@@ -77,7 +75,7 @@ static int Open( vlc_object_t * p_this )
 {
     demux_t     *p_demux = (demux_t*)p_this;
     demux_sys_t *p_sys;
-    uint8_t     *p_buf;
+    const uint8_t *p_buf;
     uint16_t    i_data_offset, i_version;
 
     if( stream_Peek( p_demux->s, &p_buf, 26 ) < 26 )
@@ -114,7 +112,7 @@ static int Open( vlc_object_t * p_this )
     if( p_sys == NULL )
         return VLC_ENOMEM;
 
-    p_sys->i_silence_countdown = p_sys->i_block_offset = p_sys->i_block_size =
+    p_sys->i_silence_countdown = p_sys->i_block_start = p_sys->i_block_end =
     p_sys->i_loop_count = 0;
     p_sys->p_es = NULL;
 
@@ -135,6 +133,29 @@ static int fmtcmp( es_format_t *ofmt, es_format_t *nfmt )
 }
 
 
+/*
+ * Converts old-style VOC sample rates to commonly used ones
+ * so as not to confuse sound card drivers.
+ * (I assume 16k, 24k and 32k are never found in .VOC files)
+ */
+static unsigned int fix_voc_sr( unsigned int sr )
+{
+    switch( sr )
+    {
+        /*case 8000:
+            return 8000;*/
+        case 11111:
+            return 11025;
+
+        case 22222:
+            return 22050;
+
+        case 44444:
+            return 44100;
+    }
+    return sr;
+}
+
 static int ReadBlockHeader( demux_t *p_demux )
 {
     es_format_t     new_fmt;
@@ -164,20 +185,20 @@ static int ReadBlockHeader( demux_t *p_demux )
             if( stream_Read( p_demux->s, buf, 2 ) < 2 )
                 goto corrupt;
 
-            new_fmt.audio.i_rate = 1000000L / (256L - buf[0]);
             if( buf[1] )
             {
-                msg_Err( p_demux, "Unsupported compression" );
+                msg_Err( p_demux, "unsupported compression" );
                 return VLC_EGENERIC;
             }
 
             new_fmt.i_codec = VLC_FOURCC('u','8',' ',' ');
+            new_fmt.audio.i_rate = fix_voc_sr( 1000000L / (256L - buf[0]) );
             new_fmt.audio.i_bytes_per_frame = 1;
             new_fmt.audio.i_frame_length = 1;
             new_fmt.audio.i_channels = 1;
             new_fmt.audio.i_blockalign = 1;
             new_fmt.audio.i_bitspersample = 8;
-            new_fmt.i_bitrate = p_sys->fmt.audio.i_rate * 8;
+            new_fmt.i_bitrate = new_fmt.audio.i_rate * 8;
             break;
 
         case 2: /* data block with same format as the previous one */
@@ -195,14 +216,14 @@ static int ReadBlockHeader( demux_t *p_demux )
             i_block_size = 0;
             p_sys->i_silence_countdown = GetWLE( buf );
 
-            new_fmt.audio.i_rate = 1000000L / (256L - buf[2]);
             new_fmt.i_codec = VLC_FOURCC('u','8',' ',' ');
+            new_fmt.audio.i_rate = fix_voc_sr( 1000000L / (256L - buf[0]) );
             new_fmt.audio.i_bytes_per_frame = 1;
             new_fmt.audio.i_frame_length = 1;
             new_fmt.audio.i_channels = 1;
             new_fmt.audio.i_blockalign = 1;
             new_fmt.audio.i_bitspersample = 8;
-            new_fmt.i_bitrate = p_sys->fmt.audio.i_rate * 8;
+            new_fmt.i_bitrate = new_fmt.audio.i_rate * 8;
             break;
 
         case 6: /* repeat block */
@@ -228,12 +249,57 @@ static int ReadBlockHeader( demux_t *p_demux )
             }
             break;
 
-        /* FIXME: support block type 8 properly */
         case 8:
-            msg_Err( p_demux, "Unimplemented block type 8" );
-            return VLC_EGENERIC;
+            /*
+             * Block 8 is a big kludge to add stereo support to block 1 :
+             * A block of type 8 is always followed by a block of type 1
+             * and specifies the number of channels in that 1-block
+             * (normally block 1 are always mono). In practice, block type 9
+             * is used for stereo rather than 8
+             */
+            if( ( i_block_size != 4 )
+             || ( stream_Read( p_demux->s, buf, 4 ) < 4 ) )
+                goto corrupt;
+
+            if( buf[2] )
+            {
+                msg_Err( p_demux, "unsupported compression" );
+                return VLC_EGENERIC;
+            }
+
+            new_fmt.i_codec = VLC_FOURCC('u','8',' ',' ');
+            new_fmt.audio.i_channels = buf[3] + 1; /* can't be nul */
+            new_fmt.audio.i_rate = 256000000L /
+                          ((65536L - GetWLE(buf)) * new_fmt.audio.i_channels);
+            new_fmt.audio.i_bytes_per_frame = new_fmt.audio.i_channels;
+            new_fmt.audio.i_frame_length = 1;
+            new_fmt.audio.i_blockalign = new_fmt.audio.i_bytes_per_frame;
+            new_fmt.audio.i_bitspersample = 8 * new_fmt.audio.i_bytes_per_frame;
+            new_fmt.i_bitrate = new_fmt.audio.i_rate * 8;
+
+            /* read subsequent block 1 */
+            if( stream_Read( p_demux->s, buf, 4 ) < 4 )
+                return VLC_EGENERIC; /* EOF */
+            i_block_size = GetDWLE( buf ) >> 8;
+            msg_Dbg( p_demux, "new block: type: %u, size: %u",
+                    (unsigned)*buf, i_block_size );
+            if( i_block_size < 2 )
+                goto corrupt;
+            i_block_size -= 2;
+
+            if( stream_Read( p_demux->s, buf, 2 ) < 2 )
+                goto corrupt;
 
-        case 9:
+            if( buf[1] )
+            {
+                msg_Err( p_demux, "unsupported compression" );
+                return VLC_EGENERIC;
+            }
+
+            break;
+
+        case 9: /* newer data block with channel number and bits resolution */
             if( i_block_size < 12 )
                 goto corrupt;
             i_block_size -= 12;
@@ -260,7 +326,7 @@ static int ReadBlockHeader( demux_t *p_demux )
                             break;
 
                         default:
-                            msg_Err( p_demux, "Unsupported bit res.: %u bits",
+                            msg_Err( p_demux, "unsupported bit res.: %u bits",
                                      new_fmt.audio.i_bitspersample );
                             return VLC_EGENERIC;
                     }
@@ -278,27 +344,27 @@ static int ReadBlockHeader( demux_t *p_demux )
                             break;
 
                         default:
-                            msg_Err( p_demux, "Unsupported bit res.: %u bits",
+                            msg_Err( p_demux, "unsupported bit res.: %u bits",
                                      new_fmt.audio.i_bitspersample );
                             return VLC_EGENERIC;
                     }
                     break;
 
-                default: 
-                    msg_Err( p_demux, "Unsupported compression" );
+                default:
+                    msg_Err( p_demux, "unsupported compression" );
                     return VLC_EGENERIC;
             }
 
             new_fmt.audio.i_bytes_per_frame = new_fmt.audio.i_channels
                 * (new_fmt.audio.i_bitspersample / 8);
             new_fmt.audio.i_frame_length = 1;
-            new_fmt.audio.i_blockalign = p_sys->fmt.audio.i_bytes_per_frame;
+            new_fmt.audio.i_blockalign = new_fmt.audio.i_bytes_per_frame;
             new_fmt.i_bitrate = 8 * new_fmt.audio.i_rate
                                      * new_fmt.audio.i_bytes_per_frame;
             break;
 
         default:
-            msg_Dbg( p_demux, "Unknown block type %u - skipping block",
+            msg_Dbg( p_demux, "unknown block type %u - skipping block",
                      (unsigned)*buf);
         case 4: /* blocks of non-audio types can be skipped */
         case 5:
@@ -308,8 +374,8 @@ static int ReadBlockHeader( demux_t *p_demux )
             break;
     }
 
-    p_sys->i_block_size = i_block_size;
-    p_sys->i_block_offset = stream_Tell( p_demux->s );
+    p_sys->i_block_start = stream_Tell( p_demux->s );
+    p_sys->i_block_end = p_sys->i_block_start + i_block_size;
 
     if( i_block_size || p_sys->i_silence_countdown )
     {
@@ -348,23 +414,29 @@ corrupt:
  *****************************************************************************
  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
  *****************************************************************************/
+#define SAMPLES_BUFFER 1000
 static int Demux( demux_t *p_demux )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     block_t     *p_block;
-    int64_t     i_offset;
+    int64_t     i_offset, i;
+
+    i_offset = stream_Tell( p_demux->s );
+
+    while( ( i_offset >= p_sys->i_block_end )
+         && ( p_sys->i_silence_countdown == 0 ) )
+        if( ReadBlockHeader( p_demux ) != VLC_SUCCESS )
+            return 0;
 
     if( p_sys->i_silence_countdown == 0 )
     {
-        i_offset = stream_Tell( p_demux->s );
-
-        while( ( i_offset >= p_sys->i_block_offset + p_sys->i_block_size )
-             && ( p_sys->i_silence_countdown == 0 ) )
-            if( ReadBlockHeader( p_demux ) != VLC_SUCCESS )
-                return 0;
+        i = ( p_sys->i_block_end - i_offset )
+            / p_sys->fmt.audio.i_bytes_per_frame;
+        if( i > SAMPLES_BUFFER )
+            i = SAMPLES_BUFFER;
 
         p_block = stream_Block( p_demux->s,
-                                p_sys->fmt.audio.i_bytes_per_frame );
+                                p_sys->fmt.audio.i_bytes_per_frame * i );
         if( p_block == NULL )
         {
             msg_Warn( p_demux, "cannot read data" );
@@ -373,16 +445,20 @@ static int Demux( demux_t *p_demux )
     }
     else
     {   /* emulates silence from the stream */
-        p_block = block_New( p_demux, 1 );
+        i = p_sys->i_silence_countdown;
+        if( i > SAMPLES_BUFFER )
+            i = SAMPLES_BUFFER;
+
+        p_block = block_New( p_demux, i );
         if( p_block == NULL )
             return VLC_ENOMEM;
 
-        p_block->p_buffer[0] = 0;
-        p_sys->i_silence_countdown--;
+        memset( p_block->p_buffer, 0, i );
+        p_sys->i_silence_countdown -= i;
     }
 
     p_block->i_dts = p_block->i_pts =
-        date_Increment( &p_sys->pts, p_sys->fmt.audio.i_frame_length );
+        date_Increment( &p_sys->pts, p_sys->fmt.audio.i_frame_length * i );
 
     es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
 
@@ -408,8 +484,8 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
 {
     demux_sys_t *p_sys  = p_demux->p_sys;
 
-    return demux2_vaControlHelper( p_demux->s, p_sys->i_block_offset,
-                                   p_sys->i_block_offset + p_sys->i_block_size,
+    return demux2_vaControlHelper( p_demux->s, p_sys->i_block_start,
+                                   p_sys->i_block_end,
                                    p_sys->fmt.i_bitrate,
                                    p_sys->fmt.audio.i_blockalign,
                                    i_query, args );