1 /*****************************************************************************
2 * file.c : audio output which writes the samples to a file
3 *****************************************************************************
4 * Copyright (C) 2002 VLC authors and VideoLAN
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 it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
39 #include <vlc_codecs.h> /* WAVEHEADER */
42 #define A52_FRAME_NB 1536
44 /*****************************************************************************
45 * aout_sys_t: audio output method descriptor
46 *****************************************************************************
47 * This structure is part of the audio output thread descriptor.
48 * It describes the direct sound specific properties of an audio device.
49 *****************************************************************************/
53 bool b_add_wav_header
;
55 WAVEHEADER waveh
; /* Wave header of the output file */
58 #define CHANNELS_MAX 6
59 static const int pi_channels_maps
[CHANNELS_MAX
+1] =
63 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
,
64 AOUT_CHAN_CENTER
| AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
,
65 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_REARLEFT
66 | AOUT_CHAN_REARRIGHT
,
67 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
68 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
,
69 AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
70 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
| AOUT_CHAN_LFE
73 /*****************************************************************************
75 *****************************************************************************/
76 static int Open ( vlc_object_t
* );
77 static void Play ( audio_output_t
*, block_t
*, vlc_tick_t
);
78 static void Pause ( audio_output_t
*, bool, vlc_tick_t
);
79 static void Flush ( audio_output_t
*, bool );
81 /*****************************************************************************
83 *****************************************************************************/
84 #define FORMAT_TEXT N_("Output format")
86 #define CHANNELS_TEXT N_("Number of output channels")
87 #define CHANNELS_LONGTEXT N_("By default (0), all the channels of the incoming " \
88 "will be saved but you can restrict the number of channels here.")
90 #define WAV_TEXT N_("Add WAVE header")
91 #define WAV_LONGTEXT N_("Instead of writing a raw file, you can add a WAV " \
92 "header to the file.")
94 static const char *const format_list
[] = {
96 #ifndef WORDS_BIGENDIAN
101 static const int format_int
[] = {
102 VLC_CODEC_U8
, VLC_CODEC_S16N
,
103 #ifndef WORDS_BIGENDIAN
109 #define FILE_TEXT N_("Output file")
110 #define FILE_LONGTEXT N_("File to which the audio samples will be written to (\"-\" for stdout).")
113 set_description( N_("File audio output") )
114 set_shortname( N_("File") )
115 set_category( CAT_AUDIO
)
116 set_subcategory( SUBCAT_AUDIO_AOUT
)
118 add_savefile("audiofile-file", "audiofile.wav", FILE_TEXT
, FILE_LONGTEXT
)
119 add_string( "audiofile-format", "s16",
120 FORMAT_TEXT
, FORMAT_TEXT
, true )
121 change_string_list( format_list
, format_list
)
122 add_integer( "audiofile-channels", 0,
123 CHANNELS_TEXT
, CHANNELS_LONGTEXT
, true )
124 change_integer_range( 0, 6 )
125 add_bool( "audiofile-wav", true, WAV_TEXT
, WAV_LONGTEXT
, true )
127 set_capability( "audio output", 0 )
128 add_shortcut( "file", "audiofile" )
129 set_callbacks( Open
, NULL
)
132 static int Start( audio_output_t
*p_aout
, audio_sample_format_t
*restrict fmt
)
134 char * psz_name
, * psz_format
;
135 const char * const * ppsz_compare
= format_list
;
136 int i_channels
, i
= 0;
138 if( aout_FormatNbChannels( fmt
) == 0 )
141 psz_name
= var_InheritString( p_aout
, "audiofile-file" );
144 msg_Err( p_aout
, "you need to specify an output file name" );
149 /* Allocate structure */
150 aout_sys_t
*p_sys
= malloc( sizeof( aout_sys_t
) );
155 if( !strcmp( psz_name
, "-" ) )
156 p_sys
->p_file
= stdout
;
158 p_sys
->p_file
= vlc_fopen( psz_name
, "wb" );
161 if ( p_sys
->p_file
== NULL
)
167 p_aout
->time_get
= aout_TimeGetDefault
;
169 p_aout
->pause
= Pause
;
170 p_aout
->flush
= Flush
;
173 psz_format
= var_InheritString( p_aout
, "audiofile-format" );
174 if ( !psz_format
) /* FIXME */
176 if( p_sys
->p_file
!= stdout
)
177 fclose( p_sys
->p_file
);
182 while ( *ppsz_compare
!= NULL
)
184 if ( !strncmp( *ppsz_compare
, psz_format
, strlen(*ppsz_compare
) ) )
191 if ( *ppsz_compare
== NULL
)
193 msg_Err( p_aout
, "cannot understand the format string (%s)",
195 if( p_sys
->p_file
!= stdout
)
196 fclose( p_sys
->p_file
);
203 fmt
->i_format
= format_int
[i
];
204 if ( AOUT_FMT_SPDIF( fmt
) )
206 fmt
->i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
207 fmt
->i_frame_length
= A52_FRAME_NB
;
210 /* Channels number */
211 i_channels
= var_InheritInteger( p_aout
, "audiofile-channels" );
212 if( i_channels
> 0 && i_channels
<= CHANNELS_MAX
)
214 fmt
->i_physical_channels
= pi_channels_maps
[i_channels
];
216 fmt
->channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
219 p_sys
->b_add_wav_header
= var_InheritBool( p_aout
, "audiofile-wav" );
220 if( p_sys
->b_add_wav_header
)
222 /* Write wave header */
223 WAVEHEADER
*wh
= &p_sys
->waveh
;
225 memset( wh
, 0, sizeof(*wh
) );
227 switch( fmt
->i_format
)
229 #ifndef WORDS_BIGENDIAN
231 wh
->Format
= WAVE_FORMAT_IEEE_FLOAT
;
232 wh
->BitsPerSample
= sizeof(float) * 8;
236 wh
->Format
= WAVE_FORMAT_PCM
;
237 wh
->BitsPerSample
= 8;
240 wh
->Format
= WAVE_FORMAT_PCM
;
241 wh
->BitsPerSample
= 16;
245 wh
->MainChunkID
= VLC_FOURCC('R', 'I', 'F', 'F');
246 wh
->Length
= 0; /* temp, to be filled in as we go */
247 wh
->ChunkTypeID
= VLC_FOURCC('W', 'A', 'V', 'E');
248 wh
->SubChunkID
= VLC_FOURCC('f', 'm', 't', ' ');
249 wh
->SubChunkLength
= 16;
251 wh
->Modus
= aout_FormatNbChannels( fmt
);
252 wh
->SampleFreq
= fmt
->i_rate
;
253 wh
->BytesPerSample
= wh
->Modus
* ( wh
->BitsPerSample
/ 8 );
254 wh
->BytesPerSec
= wh
->BytesPerSample
* wh
->SampleFreq
;
256 wh
->DataChunkID
= VLC_FOURCC('d', 'a', 't', 'a');
257 wh
->DataLength
= 0; /* temp, to be filled in as we go */
259 /* Header -> little endian format */
260 SetWLE( &wh
->Format
, wh
->Format
);
261 SetWLE( &wh
->BitsPerSample
, wh
->BitsPerSample
);
262 SetDWLE( &wh
->SubChunkLength
, wh
->SubChunkLength
);
263 SetWLE( &wh
->Modus
, wh
->Modus
);
264 SetDWLE( &wh
->SampleFreq
, wh
->SampleFreq
);
265 SetWLE( &wh
->BytesPerSample
, wh
->BytesPerSample
);
266 SetDWLE( &wh
->BytesPerSec
, wh
->BytesPerSec
);
268 if( fwrite( wh
, sizeof(WAVEHEADER
), 1,
269 p_sys
->p_file
) != 1 )
271 msg_Err( p_aout
, "write error: %s", vlc_strerror_c(errno
) );
278 /*****************************************************************************
279 * Close: close our file
280 *****************************************************************************/
281 static void Stop( audio_output_t
*p_aout
)
283 msg_Dbg( p_aout
, "closing audio file" );
284 aout_sys_t
*p_sys
= p_aout
->sys
;
286 if( p_sys
->b_add_wav_header
)
288 /* Update Wave Header */
289 p_sys
->waveh
.Length
=
290 p_sys
->waveh
.DataLength
+ sizeof(WAVEHEADER
) - 4;
292 /* Write Wave Header */
293 if( fseek( p_sys
->p_file
, 0, SEEK_SET
) )
295 msg_Err( p_aout
, "seek error: %s", vlc_strerror_c(errno
) );
298 /* Header -> little endian format */
299 SetDWLE( &p_sys
->waveh
.Length
,
300 p_sys
->waveh
.Length
);
301 SetDWLE( &p_sys
->waveh
.DataLength
,
302 p_sys
->waveh
.DataLength
);
304 if( fwrite( &p_sys
->waveh
, sizeof(WAVEHEADER
), 1,
305 p_sys
->p_file
) != 1 )
307 msg_Err( p_aout
, "write error: %s", vlc_strerror_c(errno
) );
311 if( p_sys
->p_file
!= stdout
)
312 fclose( p_sys
->p_file
);
316 /*****************************************************************************
317 * Play: pretend to play a sound
318 *****************************************************************************/
319 static void Play( audio_output_t
* p_aout
, block_t
*p_buffer
, vlc_tick_t date
)
321 aout_sys_t
*p_sys
= p_aout
->sys
;
322 if( fwrite( p_buffer
->p_buffer
, p_buffer
->i_buffer
, 1,
323 p_sys
->p_file
) != 1 )
325 msg_Err( p_aout
, "write error: %s", vlc_strerror_c(errno
) );
328 if( p_sys
->b_add_wav_header
)
330 /* Update Wave Header */
331 p_sys
->waveh
.DataLength
+= p_buffer
->i_buffer
;
334 block_Release( p_buffer
);
338 static void Pause( audio_output_t
*aout
, bool paused
, vlc_tick_t date
)
340 (void) aout
; (void) paused
; (void) date
;
343 static void Flush( audio_output_t
*aout
, bool wait
)
345 aout_sys_t
*p_sys
= aout
->sys
;
346 if( fflush( p_sys
->p_file
) )
347 msg_Err( aout
, "flush error: %s", vlc_strerror_c(errno
) );
351 static int Open(vlc_object_t
*obj
)
353 audio_output_t
*aout
= (audio_output_t
*)obj
;
357 aout
->volume_set
= NULL
;
358 aout
->mute_set
= NULL
;