1 /*****************************************************************************
2 * file.c : audio output which writes the samples to a file
3 *****************************************************************************
4 * Copyright (C) 2002 the VideoLAN team
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * Gildas Bazin <gbazin@netcourrier.com>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
36 #include <vlc_codecs.h> /* WAVEHEADER */
39 #define FRAME_SIZE 2048
40 #define A52_FRAME_NB 1536
42 /*****************************************************************************
43 * aout_sys_t: audio output method descriptor
44 *****************************************************************************
45 * This structure is part of the audio output thread descriptor.
46 * It describes the direct sound specific properties of an audio device.
47 *****************************************************************************/
51 bool b_add_wav_header
;
53 WAVEHEADER waveh
; /* Wave header of the output file */
56 #define CHANNELS_MAX 6
57 static const int pi_channels_maps
[CHANNELS_MAX
+1] =
61 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
,
62 AOUT_CHAN_CENTER
| AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
,
63 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_REARLEFT
64 | AOUT_CHAN_REARRIGHT
,
65 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
66 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
,
67 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
68 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
| AOUT_CHAN_LFE
71 /*****************************************************************************
73 *****************************************************************************/
74 static int Open ( vlc_object_t
* );
75 static void Close ( vlc_object_t
* );
76 static void Play ( aout_instance_t
* );
78 /*****************************************************************************
80 *****************************************************************************/
81 #define FORMAT_TEXT N_("Output format")
82 #define FORMAT_LONGTEXT N_("One of \"u8\", \"s8\", \"u16\", \"s16\", " \
83 "\"u16_le\", \"s16_le\", \"u16_be\", \"s16_be\", \"fixed32\", " \
84 "\"float32\" or \"spdif\"")
85 #define CHANNELS_TEXT N_("Number of output channels")
86 #define CHANNELS_LONGTEXT N_("By default, all the channels of the incoming " \
87 "will be saved but you can restrict the number of channels here.")
89 #define WAV_TEXT N_("Add WAVE header")
90 #define WAV_LONGTEXT N_("Instead of writing a raw file, you can add a WAV " \
91 "header to the file.")
93 static const char *const format_list
[] = { "u8", "s8", "u16", "s16", "u16_le",
94 "s16_le", "u16_be", "s16_be", "fixed32",
96 static const int format_int
[] = { VLC_CODEC_U8
,
98 VLC_CODEC_U16N
, VLC_CODEC_S16N
,
107 #define FILE_TEXT N_("Output file")
108 #define FILE_LONGTEXT N_("File to which the audio samples will be written to. (\"-\" for stdout")
111 set_description( N_("File audio output") )
112 set_shortname( N_("File") )
113 set_category( CAT_AUDIO
)
114 set_subcategory( SUBCAT_AUDIO_AOUT
)
116 add_string( "audiofile-format", "s16", NULL
,
117 FORMAT_TEXT
, FORMAT_LONGTEXT
, true )
118 change_string_list( format_list
, 0, 0 )
119 add_integer( "audiofile-channels", 0, NULL
,
120 CHANNELS_TEXT
, CHANNELS_LONGTEXT
, true )
121 add_file( "audiofile-file", "audiofile.wav", NULL
, FILE_TEXT
,
122 FILE_LONGTEXT
, false )
123 add_bool( "audiofile-wav", true, NULL
, WAV_TEXT
, WAV_LONGTEXT
, true )
125 set_capability( "audio output", 0 )
126 add_shortcut( "file", "audiofile" )
127 set_callbacks( Open
, Close
)
130 /*****************************************************************************
131 * Open: open a dummy audio device
132 *****************************************************************************/
133 static int Open( vlc_object_t
* p_this
)
135 aout_instance_t
* p_aout
= (aout_instance_t
*)p_this
;
136 char * psz_name
, * psz_format
;
137 const char * const * ppsz_compare
= format_list
;
138 int i_channels
, i
= 0;
140 psz_name
= var_CreateGetString( p_this
, "audiofile-file" );
141 if( !psz_name
|| !*psz_name
)
143 msg_Err( p_aout
, "you need to specify an output file name" );
148 /* Allocate structure */
149 p_aout
->output
.p_sys
= malloc( sizeof( aout_sys_t
) );
150 if( p_aout
->output
.p_sys
== NULL
)
153 if( !strcmp( psz_name
, "-" ) )
154 p_aout
->output
.p_sys
->p_file
= stdout
;
156 p_aout
->output
.p_sys
->p_file
= vlc_fopen( psz_name
, "wb" );
159 if ( p_aout
->output
.p_sys
->p_file
== NULL
)
161 free( p_aout
->output
.p_sys
);
165 p_aout
->output
.pf_play
= Play
;
168 psz_format
= var_CreateGetString( p_this
, "audiofile-format" );
170 while ( *ppsz_compare
!= NULL
)
172 if ( !strncmp( *ppsz_compare
, psz_format
, strlen(*ppsz_compare
) ) )
179 if ( *ppsz_compare
== NULL
)
181 msg_Err( p_aout
, "cannot understand the format string (%s)",
183 if( p_aout
->output
.p_sys
->p_file
!= stdout
)
184 fclose( p_aout
->output
.p_sys
->p_file
);
185 free( p_aout
->output
.p_sys
);
191 p_aout
->output
.output
.i_format
= format_int
[i
];
192 if ( AOUT_FMT_NON_LINEAR( &p_aout
->output
.output
) )
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
);
201 p_aout
->output
.i_nb_samples
= FRAME_SIZE
;
202 aout_VolumeSoftInit( p_aout
);
205 /* Channels number */
206 i_channels
= var_CreateGetInteger( p_this
, "audiofile-channels" );
208 if( i_channels
> 0 && i_channels
<= CHANNELS_MAX
)
210 p_aout
->output
.output
.i_physical_channels
=
211 pi_channels_maps
[i_channels
];
215 p_aout
->output
.p_sys
->b_add_wav_header
= var_CreateGetBool( p_this
,
218 if( p_aout
->output
.p_sys
->b_add_wav_header
)
220 /* Write wave header */
221 WAVEHEADER
*wh
= &p_aout
->output
.p_sys
->waveh
;
223 memset( wh
, 0, sizeof(*wh
) );
225 switch( p_aout
->output
.output
.i_format
)
228 wh
->Format
= WAVE_FORMAT_IEEE_FLOAT
;
229 wh
->BitsPerSample
= sizeof(float) * 8;
232 wh
->Format
= WAVE_FORMAT_PCM
;
233 wh
->BitsPerSample
= 8;
237 wh
->Format
= WAVE_FORMAT_PCM
;
238 wh
->BitsPerSample
= 16;
242 wh
->MainChunkID
= VLC_FOURCC('R', 'I', 'F', 'F');
243 wh
->Length
= 0; /* temp, to be filled in as we go */
244 wh
->ChunkTypeID
= VLC_FOURCC('W', 'A', 'V', 'E');
245 wh
->SubChunkID
= VLC_FOURCC('f', 'm', 't', ' ');
246 wh
->SubChunkLength
= 16;
248 wh
->Modus
= aout_FormatNbChannels( &p_aout
->output
.output
);
249 wh
->SampleFreq
= p_aout
->output
.output
.i_rate
;
250 wh
->BytesPerSample
= wh
->Modus
* ( wh
->BitsPerSample
/ 8 );
251 wh
->BytesPerSec
= wh
->BytesPerSample
* wh
->SampleFreq
;
253 wh
->DataChunkID
= VLC_FOURCC('d', 'a', 't', 'a');
254 wh
->DataLength
= 0; /* temp, to be filled in as we go */
256 /* Header -> little endian format */
257 SetWLE( &wh
->Format
, wh
->Format
);
258 SetWLE( &wh
->BitsPerSample
, wh
->BitsPerSample
);
259 SetDWLE( &wh
->SubChunkLength
, wh
->SubChunkLength
);
260 SetWLE( &wh
->Modus
, wh
->Modus
);
261 SetDWLE( &wh
->SampleFreq
, wh
->SampleFreq
);
262 SetWLE( &wh
->BytesPerSample
, wh
->BytesPerSample
);
263 SetDWLE( &wh
->BytesPerSec
, wh
->BytesPerSec
);
265 if( fwrite( wh
, sizeof(WAVEHEADER
), 1,
266 p_aout
->output
.p_sys
->p_file
) != 1 )
268 msg_Err( p_aout
, "write error (%m)" );
275 /*****************************************************************************
276 * Close: close our file
277 *****************************************************************************/
278 static void Close( vlc_object_t
* p_this
)
280 aout_instance_t
* p_aout
= (aout_instance_t
*)p_this
;
282 msg_Dbg( p_aout
, "closing audio file" );
284 if( p_aout
->output
.p_sys
->b_add_wav_header
)
286 /* Update Wave Header */
287 p_aout
->output
.p_sys
->waveh
.Length
=
288 p_aout
->output
.p_sys
->waveh
.DataLength
+ sizeof(WAVEHEADER
) - 4;
290 /* Write Wave Header */
291 if( fseek( p_aout
->output
.p_sys
->p_file
, 0, SEEK_SET
) )
293 msg_Err( p_aout
, "seek error (%m)" );
296 /* Header -> little endian format */
297 SetDWLE( &p_aout
->output
.p_sys
->waveh
.Length
,
298 p_aout
->output
.p_sys
->waveh
.Length
);
299 SetDWLE( &p_aout
->output
.p_sys
->waveh
.DataLength
,
300 p_aout
->output
.p_sys
->waveh
.DataLength
);
302 if( fwrite( &p_aout
->output
.p_sys
->waveh
, sizeof(WAVEHEADER
), 1,
303 p_aout
->output
.p_sys
->p_file
) != 1 )
305 msg_Err( p_aout
, "write error (%m)" );
309 if( p_aout
->output
.p_sys
->p_file
!= stdout
)
310 fclose( p_aout
->output
.p_sys
->p_file
);
311 free( p_aout
->output
.p_sys
);
314 /*****************************************************************************
315 * Play: pretend to play a sound
316 *****************************************************************************/
317 static void Play( aout_instance_t
* p_aout
)
319 aout_buffer_t
* p_buffer
;
321 p_buffer
= aout_FifoPop( p_aout
, &p_aout
->output
.fifo
);
323 if( fwrite( p_buffer
->p_buffer
, p_buffer
->i_buffer
, 1,
324 p_aout
->output
.p_sys
->p_file
) != 1 )
326 msg_Err( p_aout
, "write error (%m)" );
329 if( p_aout
->output
.p_sys
->b_add_wav_header
)
331 /* Update Wave Header */
332 p_aout
->output
.p_sys
->waveh
.DataLength
+= p_buffer
->i_buffer
;
335 aout_BufferFree( p_buffer
);