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" )
127 add_shortcut( "audiofile" )
128 set_callbacks( Open
, Close
)
131 /*****************************************************************************
132 * Open: open a dummy audio device
133 *****************************************************************************/
134 static int Open( vlc_object_t
* p_this
)
136 aout_instance_t
* p_aout
= (aout_instance_t
*)p_this
;
137 char * psz_name
, * psz_format
;
138 const char * const * ppsz_compare
= format_list
;
139 int i_channels
, i
= 0;
141 psz_name
= var_CreateGetString( p_this
, "audiofile-file" );
142 if( !psz_name
|| !*psz_name
)
144 msg_Err( p_aout
, "you need to specify an output file name" );
149 /* Allocate structure */
150 p_aout
->output
.p_sys
= malloc( sizeof( aout_sys_t
) );
151 if( p_aout
->output
.p_sys
== NULL
)
154 if( !strcmp( psz_name
, "-" ) )
155 p_aout
->output
.p_sys
->p_file
= stdout
;
157 p_aout
->output
.p_sys
->p_file
= vlc_fopen( psz_name
, "wb" );
160 if ( p_aout
->output
.p_sys
->p_file
== NULL
)
162 free( p_aout
->output
.p_sys
);
166 p_aout
->output
.pf_play
= Play
;
169 psz_format
= var_CreateGetString( p_this
, "audiofile-format" );
171 while ( *ppsz_compare
!= NULL
)
173 if ( !strncmp( *ppsz_compare
, psz_format
, strlen(*ppsz_compare
) ) )
180 if ( *ppsz_compare
== NULL
)
182 msg_Err( p_aout
, "cannot understand the format string (%s)",
184 if( p_aout
->output
.p_sys
->p_file
!= stdout
)
185 fclose( p_aout
->output
.p_sys
->p_file
);
186 free( p_aout
->output
.p_sys
);
192 p_aout
->output
.output
.i_format
= format_int
[i
];
193 if ( AOUT_FMT_NON_LINEAR( &p_aout
->output
.output
) )
195 p_aout
->output
.i_nb_samples
= A52_FRAME_NB
;
196 p_aout
->output
.output
.i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
197 p_aout
->output
.output
.i_frame_length
= A52_FRAME_NB
;
198 aout_VolumeNoneInit( p_aout
);
202 p_aout
->output
.i_nb_samples
= FRAME_SIZE
;
203 aout_VolumeSoftInit( p_aout
);
206 /* Channels number */
207 i_channels
= var_CreateGetInteger( p_this
, "audiofile-channels" );
209 if( i_channels
> 0 && i_channels
<= CHANNELS_MAX
)
211 p_aout
->output
.output
.i_physical_channels
=
212 pi_channels_maps
[i_channels
];
216 p_aout
->output
.p_sys
->b_add_wav_header
= var_CreateGetBool( p_this
,
219 if( p_aout
->output
.p_sys
->b_add_wav_header
)
221 /* Write wave header */
222 WAVEHEADER
*wh
= &p_aout
->output
.p_sys
->waveh
;
224 memset( wh
, 0, sizeof(*wh
) );
226 switch( p_aout
->output
.output
.i_format
)
229 wh
->Format
= WAVE_FORMAT_IEEE_FLOAT
;
230 wh
->BitsPerSample
= sizeof(float) * 8;
233 wh
->Format
= WAVE_FORMAT_PCM
;
234 wh
->BitsPerSample
= 8;
238 wh
->Format
= WAVE_FORMAT_PCM
;
239 wh
->BitsPerSample
= 16;
243 wh
->MainChunkID
= VLC_FOURCC('R', 'I', 'F', 'F');
244 wh
->Length
= 0; /* temp, to be filled in as we go */
245 wh
->ChunkTypeID
= VLC_FOURCC('W', 'A', 'V', 'E');
246 wh
->SubChunkID
= VLC_FOURCC('f', 'm', 't', ' ');
247 wh
->SubChunkLength
= 16;
249 wh
->Modus
= aout_FormatNbChannels( &p_aout
->output
.output
);
250 wh
->SampleFreq
= p_aout
->output
.output
.i_rate
;
251 wh
->BytesPerSample
= wh
->Modus
* ( wh
->BitsPerSample
/ 8 );
252 wh
->BytesPerSec
= wh
->BytesPerSample
* wh
->SampleFreq
;
254 wh
->DataChunkID
= VLC_FOURCC('d', 'a', 't', 'a');
255 wh
->DataLength
= 0; /* temp, to be filled in as we go */
257 /* Header -> little endian format */
258 SetWLE( &wh
->Format
, wh
->Format
);
259 SetWLE( &wh
->BitsPerSample
, wh
->BitsPerSample
);
260 SetDWLE( &wh
->SubChunkLength
, wh
->SubChunkLength
);
261 SetWLE( &wh
->Modus
, wh
->Modus
);
262 SetDWLE( &wh
->SampleFreq
, wh
->SampleFreq
);
263 SetWLE( &wh
->BytesPerSample
, wh
->BytesPerSample
);
264 SetDWLE( &wh
->BytesPerSec
, wh
->BytesPerSec
);
266 if( fwrite( wh
, sizeof(WAVEHEADER
), 1,
267 p_aout
->output
.p_sys
->p_file
) != 1 )
269 msg_Err( p_aout
, "write error (%m)" );
276 /*****************************************************************************
277 * Close: close our file
278 *****************************************************************************/
279 static void Close( vlc_object_t
* p_this
)
281 aout_instance_t
* p_aout
= (aout_instance_t
*)p_this
;
283 msg_Dbg( p_aout
, "closing audio file" );
285 if( p_aout
->output
.p_sys
->b_add_wav_header
)
287 /* Update Wave Header */
288 p_aout
->output
.p_sys
->waveh
.Length
=
289 p_aout
->output
.p_sys
->waveh
.DataLength
+ sizeof(WAVEHEADER
) - 4;
291 /* Write Wave Header */
292 if( fseek( p_aout
->output
.p_sys
->p_file
, 0, SEEK_SET
) )
294 msg_Err( p_aout
, "seek error (%m)" );
297 /* Header -> little endian format */
298 SetDWLE( &p_aout
->output
.p_sys
->waveh
.Length
,
299 p_aout
->output
.p_sys
->waveh
.Length
);
300 SetDWLE( &p_aout
->output
.p_sys
->waveh
.DataLength
,
301 p_aout
->output
.p_sys
->waveh
.DataLength
);
303 if( fwrite( &p_aout
->output
.p_sys
->waveh
, sizeof(WAVEHEADER
), 1,
304 p_aout
->output
.p_sys
->p_file
) != 1 )
306 msg_Err( p_aout
, "write error (%m)" );
310 if( p_aout
->output
.p_sys
->p_file
!= stdout
)
311 fclose( p_aout
->output
.p_sys
->p_file
);
312 free( p_aout
->output
.p_sys
);
315 /*****************************************************************************
316 * Play: pretend to play a sound
317 *****************************************************************************/
318 static void Play( aout_instance_t
* p_aout
)
320 aout_buffer_t
* p_buffer
;
322 p_buffer
= aout_FifoPop( p_aout
, &p_aout
->output
.fifo
);
324 if( fwrite( p_buffer
->p_buffer
, p_buffer
->i_buffer
, 1,
325 p_aout
->output
.p_sys
->p_file
) != 1 )
327 msg_Err( p_aout
, "write error (%m)" );
330 if( p_aout
->output
.p_sys
->b_add_wav_header
)
332 /* Update Wave Header */
333 p_aout
->output
.p_sys
->waveh
.DataLength
+= p_buffer
->i_buffer
;
336 aout_BufferFree( p_buffer
);