Fix warnings.
[vlc.git] / modules / audio_output / file.c
1 /*****************************************************************************
2  * file.c : audio output which writes the samples to a file
3  *****************************************************************************
4  * Copyright (C) 2002 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Gildas Bazin <gbazin@netcourrier.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc_aout.h>
34 #include <vlc_codecs.h> /* WAVEHEADER */
35 #include <vlc_charset.h>
36
37 #define FRAME_SIZE 2048
38 #define A52_FRAME_NB 1536
39
40 /*****************************************************************************
41  * aout_sys_t: audio output method descriptor
42  *****************************************************************************
43  * This structure is part of the audio output thread descriptor.
44  * It describes the direct sound specific properties of an audio device.
45  *****************************************************************************/
46 struct aout_sys_t
47 {
48     FILE     * p_file;
49     vlc_bool_t b_add_wav_header;
50
51     WAVEHEADER waveh;                      /* Wave header of the output file */
52 };
53
54 #define CHANNELS_MAX 6
55 static int pi_channels_maps[CHANNELS_MAX+1] =
56 {
57     0,
58     AOUT_CHAN_CENTER,
59     AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
60     AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
61     AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
62      | AOUT_CHAN_REARRIGHT,
63     AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
64      | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT,
65     AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
66      | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE
67 };
68
69 /*****************************************************************************
70  * Local prototypes.
71  *****************************************************************************/
72 static int     Open        ( vlc_object_t * );
73 static void    Close       ( vlc_object_t * );
74 static void    Play        ( aout_instance_t * );
75
76 /*****************************************************************************
77  * Module descriptor
78  *****************************************************************************/
79 #define FORMAT_TEXT N_("Output format")
80 #define FORMAT_LONGTEXT N_("One of \"u8\", \"s8\", \"u16\", \"s16\", " \
81     "\"u16_le\", \"s16_le\", \"u16_be\", \"s16_be\", \"fixed32\", " \
82     "\"float32\" or \"spdif\"")
83 #define CHANNELS_TEXT N_("Number of output channels")
84 #define CHANNELS_LONGTEXT N_("By default, all the channels of the incoming " \
85     "will be saved but you can restrict the number of channels here.")
86
87 #define WAV_TEXT N_("Add WAVE header")
88 #define WAV_LONGTEXT N_("Instead of writing a raw file, you can add a WAV " \
89                         "header to the file.")
90
91 static const char *format_list[] = { "u8", "s8", "u16", "s16", "u16_le",
92                                      "s16_le", "u16_be", "s16_be", "fixed32",
93                                      "float32", "spdif" };
94 static int format_int[] = { VLC_FOURCC('u','8',' ',' '),
95                             VLC_FOURCC('s','8',' ',' '),
96                             AOUT_FMT_U16_NE, AOUT_FMT_S16_NE,
97                             VLC_FOURCC('u','1','6','l'),
98                             VLC_FOURCC('s','1','6','l'),
99                             VLC_FOURCC('u','1','6','b'),
100                             VLC_FOURCC('s','1','6','b'),
101                             VLC_FOURCC('f','i','3','2'),
102                             VLC_FOURCC('f','l','3','2'),
103                             VLC_FOURCC('s','p','i','f') };
104
105 #define FILE_TEXT N_("Output file")
106 #define FILE_LONGTEXT N_("File to which the audio samples will be written to.")
107
108 vlc_module_begin();
109     set_description( _("File audio output") );
110     set_shortname( _("File") );
111     set_category( CAT_AUDIO );
112     set_subcategory( SUBCAT_AUDIO_AOUT );
113
114     add_string( "audiofile-format", "s16", NULL,
115                 FORMAT_TEXT, FORMAT_LONGTEXT, VLC_TRUE );
116         change_string_list( format_list, 0, 0 );
117     add_integer( "audiofile-channels", 0, NULL,
118                  CHANNELS_TEXT, CHANNELS_LONGTEXT, VLC_TRUE );
119     add_file( "audiofile-file", "audiofile.wav", NULL, FILE_TEXT,
120               FILE_LONGTEXT, VLC_FALSE );
121     add_bool( "audiofile-wav", 1, NULL, WAV_TEXT, WAV_LONGTEXT, VLC_TRUE );
122
123     set_capability( "audio output", 0 );
124     add_shortcut( "file" );
125     add_shortcut( "audiofile" );
126     set_callbacks( Open, Close );
127 vlc_module_end();
128
129 /*****************************************************************************
130  * Open: open a dummy audio device
131  *****************************************************************************/
132 static int Open( vlc_object_t * p_this )
133 {
134     aout_instance_t * p_aout = (aout_instance_t *)p_this;
135     char * psz_name, * psz_format;
136     const char ** ppsz_compare = format_list;
137     vlc_value_t val;
138     int i_channels, i = 0;
139
140     var_Create( p_this, "audiofile-file", VLC_VAR_STRING|VLC_VAR_DOINHERIT );
141     var_Get( p_this, "audiofile-file", &val );
142     psz_name = val.psz_string;
143     if( !psz_name || !*psz_name )
144     {
145         msg_Err( p_aout, "you need to specify an output file name" );
146         if( psz_name ) free( psz_name );
147         return VLC_EGENERIC;
148     }
149
150     /* Allocate structure */
151     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
152     if( p_aout->output.p_sys == NULL )
153     {
154         msg_Err( p_aout, "out of memory" );
155         return VLC_EGENERIC;
156     }
157
158     p_aout->output.p_sys->p_file = utf8_fopen( psz_name, "wb" );
159     free( psz_name );
160     if ( p_aout->output.p_sys->p_file == NULL )
161     {
162         free( p_aout->output.p_sys );
163         return VLC_EGENERIC;
164     }
165
166     p_aout->output.pf_play = Play;
167
168     /* Audio format */
169     var_Create( p_this, "audiofile-format", VLC_VAR_STRING|VLC_VAR_DOINHERIT );
170     var_Get( p_this, "audiofile-format", &val );
171     psz_format = val.psz_string;
172
173     while ( *ppsz_compare != NULL )
174     {
175         if ( !strncmp( *ppsz_compare, psz_format, strlen(*ppsz_compare) ) )
176         {
177             break;
178         }
179         ppsz_compare++; i++;
180     }
181
182     if ( *ppsz_compare == NULL )
183     {
184         msg_Err( p_aout, "cannot understand the format string (%s)",
185                  psz_format );
186         fclose( p_aout->output.p_sys->p_file );
187         free( p_aout->output.p_sys );
188         return VLC_EGENERIC;
189     }
190
191     p_aout->output.output.i_format = format_int[i];
192     if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
193     {
194         p_aout->output.i_nb_samples = A52_FRAME_NB;
195         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
196         p_aout->output.output.i_frame_length = A52_FRAME_NB;
197         aout_VolumeNoneInit( p_aout );
198     }
199     else
200     {
201         p_aout->output.i_nb_samples = FRAME_SIZE;
202         aout_VolumeSoftInit( p_aout );
203     }
204
205     /* Channels number */
206     var_Create( p_this, "audiofile-channels",
207                 VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
208     var_Get( p_this, "audiofile-channels", &val );
209     i_channels = val.i_int;
210
211     if( i_channels > 0 && i_channels <= CHANNELS_MAX )
212     {
213         p_aout->output.output.i_physical_channels =
214             pi_channels_maps[i_channels];
215     }
216
217     /* WAV header */
218     var_Create( p_this, "audiofile-wav", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
219     var_Get( p_this, "audiofile-wav", &val );
220     p_aout->output.p_sys->b_add_wav_header = val.b_bool;
221
222     if( p_aout->output.p_sys->b_add_wav_header )
223     {
224         /* Write wave header */
225         WAVEHEADER *wh = &p_aout->output.p_sys->waveh;
226
227         memset( wh, 0, sizeof(wh) );
228
229         switch( p_aout->output.output.i_format )
230         {
231         case VLC_FOURCC('f','l','3','2'):
232             wh->Format     = WAVE_FORMAT_IEEE_FLOAT;
233             wh->BitsPerSample = sizeof(float) * 8;
234             break;
235         case VLC_FOURCC('u','8',' ',' '):
236             wh->Format     = WAVE_FORMAT_PCM;
237             wh->BitsPerSample = 8;
238             break;
239         case VLC_FOURCC('s','1','6','l'):
240         default:
241             wh->Format     = WAVE_FORMAT_PCM;
242             wh->BitsPerSample = 16;
243             break;
244         }
245
246         wh->MainChunkID = VLC_FOURCC('R', 'I', 'F', 'F');
247         wh->Length = 0;                    /* temp, to be filled in as we go */
248         wh->ChunkTypeID = VLC_FOURCC('W', 'A', 'V', 'E');
249         wh->SubChunkID = VLC_FOURCC('f', 'm', 't', ' ');
250         wh->SubChunkLength = 16;
251
252         wh->Modus = aout_FormatNbChannels( &p_aout->output.output );
253         wh->SampleFreq = p_aout->output.output.i_rate;
254         wh->BytesPerSample = wh->Modus * ( wh->BitsPerSample / 8 );
255         wh->BytesPerSec = wh->BytesPerSample * wh->SampleFreq;
256
257         wh->DataChunkID = VLC_FOURCC('d', 'a', 't', 'a');
258         wh->DataLength = 0;                /* temp, to be filled in as we go */
259
260         /* Header -> little endian format */
261         SetWLE( &wh->Format, wh->Format );
262         SetWLE( &wh->BitsPerSample, wh->BitsPerSample );
263         SetDWLE( &wh->SubChunkLength, wh->SubChunkLength );
264         SetWLE( &wh->Modus, wh->Modus );
265         SetDWLE( &wh->SampleFreq, wh->SampleFreq );
266         SetWLE( &wh->BytesPerSample, wh->BytesPerSample );
267         SetDWLE( &wh->BytesPerSec, wh->BytesPerSec );
268
269         if( fwrite( wh, sizeof(WAVEHEADER), 1,
270                     p_aout->output.p_sys->p_file ) != 1 )
271         {
272             msg_Err( p_aout, "write error (%s)", strerror(errno) );
273         }
274     }
275
276     return 0;
277 }
278
279 /*****************************************************************************
280  * Close: close our file
281  *****************************************************************************/
282 static void Close( vlc_object_t * p_this )
283 {
284     aout_instance_t * p_aout = (aout_instance_t *)p_this;
285
286     msg_Dbg( p_aout, "closing audio file" );
287
288     if( p_aout->output.p_sys->b_add_wav_header )
289     {
290         /* Update Wave Header */
291         p_aout->output.p_sys->waveh.Length =
292             p_aout->output.p_sys->waveh.DataLength + sizeof(WAVEHEADER) - 4;
293
294         /* Write Wave Header */
295         if( fseek( p_aout->output.p_sys->p_file, 0, SEEK_SET ) )
296         {
297             msg_Err( p_aout, "seek error (%s)", strerror(errno) );
298         }
299
300         /* Header -> little endian format */
301         SetDWLE( &p_aout->output.p_sys->waveh.Length,
302                  p_aout->output.p_sys->waveh.Length );
303         SetDWLE( &p_aout->output.p_sys->waveh.DataLength,
304                  p_aout->output.p_sys->waveh.DataLength );
305
306         if( fwrite( &p_aout->output.p_sys->waveh, sizeof(WAVEHEADER), 1,
307                     p_aout->output.p_sys->p_file ) != 1 )
308         {
309             msg_Err( p_aout, "write error (%s)", strerror(errno) );
310         }
311     }
312
313     fclose( p_aout->output.p_sys->p_file );
314     free( p_aout->output.p_sys );
315 }
316
317 /*****************************************************************************
318  * Play: pretend to play a sound
319  *****************************************************************************/
320 static void Play( aout_instance_t * p_aout )
321 {
322     aout_buffer_t * p_buffer;
323
324     p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
325
326     if( fwrite( p_buffer->p_buffer, p_buffer->i_nb_bytes, 1,
327                 p_aout->output.p_sys->p_file ) != 1 )
328     {
329         msg_Err( p_aout, "write error (%s)", strerror(errno) );
330     }
331
332     if( p_aout->output.p_sys->b_add_wav_header )
333     {
334         /* Update Wave Header */
335         p_aout->output.p_sys->waveh.DataLength += p_buffer->i_nb_bytes;
336     }
337
338     aout_BufferFree( p_buffer );
339 }