Created quality modes for wideband, updated encoder. Getting close to 0.4.0
[speexdsp.git] / src / speexdec.c
1 /* Copyright (C) 2002 Jean-Marc Valin 
2    File: speexdec.c
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8    
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13    
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "speex.h"
26 #include "ogg/ogg.h"
27
28 #ifdef HAVE_SYS_SOUNDCARD_H
29 #include <sys/soundcard.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <sys/ioctl.h>
34 #endif
35
36 #include <string.h>
37 #include "wav_io.h"
38 #include "speex_header.h"
39 #include "misc.h"
40
41 #define MAX_FRAME_SIZE 2000
42
43 FILE *out_file_open(char *outFile, int rate)
44 {
45    FILE *fout;
46    /*Open output file*/
47    if (strlen(outFile)==0)
48    {
49 #ifdef HAVE_SYS_SOUNDCARD_H
50       int audio_fd, format, stereo;
51       audio_fd=open("/dev/dsp", O_WRONLY);
52       
53       format=AFMT_S16_LE;
54       if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
55       {
56          perror("SNDCTL_DSP_SETFMT");
57          close(audio_fd);
58          exit(1);
59       }
60       
61       stereo=0;
62       if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
63       {
64          perror("SNDCTL_DSP_STEREO");
65          close(audio_fd);
66          exit(1);
67       }
68       if (stereo!=0)
69       {
70          fprintf (stderr, "Cannot set mono mode\n");
71          exit(1);
72       }
73
74       if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
75       {
76          perror("SNDCTL_DSP_SPEED");
77          close(audio_fd);
78          exit(1);
79       }
80       fout = fdopen(audio_fd, "w");
81 #else
82       fprintf (stderr, "No soundcard support\n");
83       exit(1);
84 #endif
85    } else {
86       if (strcmp(outFile,"-")==0)
87          fout=stdout;
88       else 
89       {
90          fout = fopen(outFile, "w");
91          if (!fout)
92          {
93             perror(outFile);
94             exit(1);
95          }
96          if (strcmp(outFile+strlen(outFile)-4,".wav")==0)
97             write_wav_header(fout, rate, 1, 0, 0);
98       }
99    }
100    return fout;
101 }
102
103 void usage()
104 {
105    fprintf (stderr, "speexenc [options] <input file> <output file>\n");
106    fprintf (stderr, "options:\n");
107    fprintf (stderr, "\t--help       -h      This help\n"); 
108    fprintf (stderr, "\t--version    -v      Version information\n"); 
109    fprintf (stderr, "\t--pf                 Enable post-filter\n"); 
110    fprintf (stderr, "\t--no-pf              Disable post-filter\n");
111 }
112
113 void version()
114 {
115    fprintf (stderr, "Speex decoder version " VERSION "\n");
116 }
117
118 static void *process_header(ogg_packet *op, int pf_enabled, int *frame_size, int *rate)
119 {
120    void *st;
121    SpeexMode *mode;
122    SpeexHeader *header;
123    
124    header = speex_packet_to_header((char*)op->packet, op->bytes);
125    if (!header)
126    {
127       fprintf (stderr, "Cannot read header\n");
128       return NULL;
129    }
130    if (header->mode >= SPEEX_NB_MODES)
131    {
132       fprintf (stderr, "Mode number %d does not (any longer) exist in this version\n", 
133                header->mode);
134       return NULL;
135    }
136       
137    mode = speex_mode_list[header->mode];
138    
139    if (mode->bitstream_version < header->mode_bitstream_version)
140    {
141       fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
142       return NULL;
143    }
144    if (mode->bitstream_version > header->mode_bitstream_version) 
145    {
146       fprintf (stderr, "The file was encoded with an older version of Speex. You would need to downgrade the version in order to play it.\n");
147       return NULL;
148    }
149    
150    st = speex_decoder_init(mode);
151    speex_decoder_ctl(st, SPEEX_SET_PF, &pf_enabled);
152    speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
153    
154    *rate = header->rate;
155
156    fprintf (stderr, "Decoding %d Hz audio using %s mode\n", 
157             *rate, mode->bitrate, mode->modeName);
158
159    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
160     *rate, mode->bitrate, mode->modeName);*/
161
162    free(header);
163    return st;
164 }
165
166 int main(int argc, char **argv)
167 {
168    int c;
169    int option_index = 0;
170    char *inFile, *outFile;
171    FILE *fin, *fout=NULL;
172    short out[MAX_FRAME_SIZE];
173    float output[MAX_FRAME_SIZE];
174    int frame_size=0;
175    void *st=NULL;
176    SpeexBits bits;
177    int packet_count=0;
178    struct option long_options[] =
179    {
180       {"help", no_argument, NULL, 0},
181       {"version", no_argument, NULL, 0},
182       {"pf", no_argument, NULL, 0},
183       {"no-pf", no_argument, NULL, 0},
184       {0, 0, 0, 0}
185    };
186    ogg_sync_state oy;
187    ogg_page       og;
188    ogg_packet     op;
189    ogg_stream_state os;
190    int pf_enabled;
191
192    pf_enabled = 0;
193
194    /*Process options*/
195    while(1)
196    {
197       c = getopt_long (argc, argv, "hv",
198                        long_options, &option_index);
199       if (c==-1)
200          break;
201       
202       switch(c)
203       {
204       case 0:
205          if (strcmp(long_options[option_index].name,"help")==0)
206          {
207             usage();
208             exit(0);
209          } else if (strcmp(long_options[option_index].name,"version")==0)
210          {
211             version();
212             exit(0);
213          } else if (strcmp(long_options[option_index].name,"pf")==0)
214          {
215             pf_enabled=1;
216          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
217          {
218             pf_enabled=0;
219          }
220          break;
221       case 'h':
222          usage();
223          exit(0);
224          break;
225       case 'v':
226          version();
227          exit(0);
228          break;
229       case '?':
230          usage();
231          exit(1);
232          break;
233       }
234    }
235    if (argc-optind!=2 && argc-optind!=1)
236    {
237       usage();
238       exit(1);
239    }
240    inFile=argv[optind];
241
242    if (argc-optind==2)
243       outFile=argv[optind+1];
244    else
245       outFile = "";
246    /*Open input file*/
247    if (strcmp(inFile, "-")==0)
248       fin=stdin;
249    else 
250    {
251       fin = fopen(inFile, "r");
252       if (!fin)
253       {
254          perror(inFile);
255          exit(1);
256       }
257    }
258
259
260    /*Init Ogg data struct*/
261    ogg_sync_init(&oy);
262    ogg_stream_init(&os, 0);
263    
264    speex_bits_init(&bits);
265    /*Main decoding loop*/
266    while (1)
267    {
268       char *data;
269       int i, nb_read;
270       /*Get the ogg buffer for writing*/
271       data = ogg_sync_buffer(&oy, 200);
272       /*Read bitstream from input file*/
273       nb_read = fread(data, sizeof(char), 200, fin);      
274       ogg_sync_wrote(&oy, nb_read);
275
276       /*Loop for all complete pages we got (most likely only one)*/
277       while (ogg_sync_pageout(&oy, &og)==1)
278       {
279          /*Add page to the bitstream*/
280          ogg_stream_pagein(&os, &og);
281          /*Extract all available packets*/
282          while (ogg_stream_packetout(&os, &op)==1)
283          {
284             /*If first packet, process as Speex header*/
285             if (packet_count==0)
286             {
287                int rate;
288                st = process_header(&op, pf_enabled, &frame_size, &rate);
289                if (!st)
290                   exit(1);
291                fout = out_file_open(outFile, rate);
292
293             } else if (packet_count==1){
294                fprintf (stderr, "File comments: ");
295                fwrite(op.packet, 1, op.bytes, stderr);
296                fprintf (stderr, "\n");
297             } else {
298
299                /*End of stream condition*/
300                if (strncmp((char *)op.packet, "END OF STREAM", 13)==0)
301                   break;
302
303                /* Put 0 here only to simulate packet loss */
304                if (1)
305                {
306                   /*Copy Ogg packet to Speex bitstream*/
307                   speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
308                   /*Decode a frame*/
309                   speex_decode(st, &bits, output, 0);
310                } else {
311                   static int first=1;
312                   if ((((float)rand())/RAND_MAX < .1) && !first)
313                   {
314                      printf ("PACKET LOSS\n");
315                      speex_bits_rewind(&bits);
316                      speex_decode(st, &bits, output, 1);
317                   } else {
318                      printf ("PACKET OK\n");
319                      speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
320                      speex_decode(st, &bits, output, 0);
321                   }
322                   first=0;
323                }
324                /*PCM saturation (just in case)*/
325                for (i=0;i<frame_size;i++)
326                {
327                   if (output[i]>32000)
328                      output[i]=32000;
329                   else if (output[i]<-32000)
330                      output[i]=-32000;
331                }
332                /*Convert to short and save to output file*/
333                for (i=0;i<frame_size;i++)
334                   out[i]=(short)le_short(output[i]);
335                fwrite(out, sizeof(short), frame_size, fout);
336             }
337             packet_count++;
338          }
339       }
340       if (feof(fin))
341          break;
342
343    }
344
345    if (st)
346       speex_decoder_destroy(st);
347    speex_bits_destroy(&bits);
348    ogg_stream_clear(&os);
349  
350    exit(0);
351    return 1;
352 }