2 Copyright (c) 2005-2009, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file wavesplit.cpp
28 \version $Id: wavesplit.cpp,v 1.11 2010/02/16 18:40:57 jhurst Exp $
29 \brief WAV file splitter
33 #include <WavFileWriter.h>
36 using namespace ASDCP;
38 //------------------------------------------------------------------------------------------
40 // command line option parser class
42 static const char* PROGRAM_NAME = "wavesplit"; // program name for messages
44 // Macros used to test command option data state.
46 // True if a major mode has already been selected.
47 #define TEST_MAJOR_MODE() ( create_flag || info_flag )
49 // Causes the caller to return if a major mode has already been selected,
50 // otherwise sets the given flag.
51 #define TEST_SET_MAJOR_MODE(f) if ( TEST_MAJOR_MODE() ) \
53 fputs("Conflicting major mode, choose one of -(ic).\n", stderr); \
58 // Increment the iterator, test for an additional non-option command line argument.
59 // Causes the caller to return if there are no remaining arguments or if the next
60 // argument begins with '-'.
61 #define TEST_EXTRA_ARG(i,c) if ( ++i >= argc || argv[(i)][0] == '-' ) \
63 fprintf(stderr, "Argument not found for option %c.\n", (c)); \
68 banner(FILE* stream = stderr)
72 Copyright (c) 2005-2009 John Hurst\n\n\
73 %s is part of asdcplib.\n\
74 asdcplib may be copied only under the terms of the license found at\n\
75 the top of every file in the asdcplib distribution kit.\n\n\
76 Specify the -h (help) option for further information about %s\n\n",
77 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME, PROGRAM_NAME);
82 usage(FILE* stream = stderr)
86 %s [-v] -c <root-name> [-d <duration>] [-f <start-frame>] <filename>\n\
87 %s [-v] -i <filename>\n\
90 -c <root-name> - Create a WAV file for each channel in the input file\n\
91 (default is two-channel files)\n\
92 -d <duration> - Number of frames to process, default all\n\
93 -f <frame-num> - Starting frame number, default 0\n\
95 -i - Show input file metadata (no output created)\n\
97 -v - Print extra info while processing\n\
99 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
100 o All option arguments must be separated from the option by whitespace.\n\
101 \n", PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
111 bool error_flag; // true if the given options are in error or not complete
112 bool create_flag; // true if the file create mode was selected
113 bool info_flag; // true if the file info mode was selected
114 bool version_flag; // true if the version display option was selected
115 bool help_flag; // true if the help display option was selected
116 bool verbose_flag; // true for extra info during procesing
117 ui32_t start_frame; // frame number to begin processing
118 ui32_t duration; // number of frames to be processed
119 const char* file_root; // filename prefix for files written by the extract mode
120 const char* filename; // filename to be processed
122 CommandOptions(int argc, const char** argv) :
123 error_flag(true), create_flag(false), info_flag(false),
124 version_flag(false), help_flag(false), start_frame(0),
125 duration(0xffffffff), file_root(0), filename(0)
127 for ( int i = 1; i < argc; i++ )
129 if ( argv[i][0] == '-' && isalpha(argv[i][1]) && argv[i][2] == 0 )
131 switch ( argv[i][1] )
134 TEST_SET_MAJOR_MODE(create_flag);
135 TEST_EXTRA_ARG(i, 'c');
140 TEST_EXTRA_ARG(i, 'd');
141 duration = atoi(argv[i]); // TODO: test for negative value, should use strtol()
145 TEST_EXTRA_ARG(i, 'f');
146 start_frame = atoi(argv[i]); // TODO: test for negative value, should use strtol()
149 case 'h': help_flag = true; break;
150 case 'i': TEST_SET_MAJOR_MODE(info_flag); break;
151 case 'V': version_flag = true; break;
154 fprintf(stderr, "Unrecognized option: %c\n", argv[i][1]);
162 fprintf(stderr, "Unexpected extra filename.\n");
170 if ( TEST_MAJOR_MODE() )
174 fputs("Input filename required.\n", stderr);
179 if ( ! TEST_MAJOR_MODE() && ! help_flag && ! version_flag )
181 fputs("No operation selected (use one of -(ic) or -h for help).\n", stderr);
191 wav_file_info(CommandOptions& Options)
193 PCM::AudioDescriptor ADesc;
194 Rational PictureRate = EditRate_24;
195 PCM::WAVParser Parser;
197 // set up essence parser
198 Result_t result = Parser.OpenRead(Options.filename, PictureRate);
200 if ( ASDCP_SUCCESS(result) )
202 Parser.FillAudioDescriptor(ADesc);
203 ADesc.EditRate = PictureRate;
204 fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n", "24",
205 PCM::CalcSamplesPerFrame(ADesc));
206 fputs("AudioDescriptor:\n", stderr);
207 PCM::AudioDescriptorDump(ADesc);
215 split_buffer(ui32_t sample_size, PCM::FrameBuffer& FrameBuffer,
216 PCM::FrameBuffer& L_FrameBuffer, PCM::FrameBuffer& R_FrameBuffer)
218 assert((FrameBuffer.Size() % 2) == 0);
219 byte_t* p = FrameBuffer.Data();
220 byte_t* end_p = p + FrameBuffer.Size();
221 byte_t* lp = L_FrameBuffer.Data();
222 byte_t* rp = R_FrameBuffer.Data();
226 memcpy(lp, p, sample_size);
229 memcpy(rp, p, sample_size);
234 L_FrameBuffer.Size(L_FrameBuffer.Capacity());
235 R_FrameBuffer.Size(R_FrameBuffer.Capacity());
240 split_wav_file(CommandOptions& Options)
242 PCM::FrameBuffer FrameBuffer;
243 PCM::FrameBuffer L_FrameBuffer;
244 PCM::FrameBuffer R_FrameBuffer;
245 PCM::AudioDescriptor ADesc;
246 Rational PictureRate = EditRate_24;
247 PCM::WAVParser Parser;
249 // set up essence parser
250 Result_t result = Parser.OpenRead(Options.filename, PictureRate);
252 if ( ASDCP_SUCCESS(result) )
254 Parser.FillAudioDescriptor(ADesc);
256 ADesc.EditRate = PictureRate;
257 ui32_t fb_size = PCM::CalcFrameBufferSize(ADesc);
258 assert((fb_size % 2) == 0);
259 FrameBuffer.Capacity(fb_size);
260 L_FrameBuffer.Capacity(fb_size/2);
261 R_FrameBuffer.Capacity(fb_size/2);
263 if ( Options.verbose_flag )
265 fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n", "24",
266 PCM::CalcSamplesPerFrame(ADesc));
267 fputs("AudioDescriptor:\n", stderr);
268 PCM::AudioDescriptorDump(ADesc);
271 ADesc.ChannelCount = 1;
274 // set up output files
275 Kumu::FileWriter L_OutFile;
276 Kumu::FileWriter R_OutFile;
278 if ( ASDCP_SUCCESS(result) )
281 snprintf(filename, 256, "%s_l.wav", Options.file_root);
282 result = L_OutFile.OpenWrite(filename);
284 if ( ASDCP_SUCCESS(result) )
286 snprintf(filename, 256, "%s_r.wav", Options.file_root);
287 result = R_OutFile.OpenWrite(filename);
292 if ( ASDCP_SUCCESS(result) )
294 Wav::SimpleWaveHeader WavHeader(ADesc);
295 result = WavHeader.WriteToFile(L_OutFile);
297 if ( ASDCP_SUCCESS(result) )
298 result = WavHeader.WriteToFile(R_OutFile);
301 if ( ASDCP_SUCCESS(result) )
303 ui32_t write_count = 0;
306 while ( ASDCP_SUCCESS(result) && (duration++ < Options.duration) )
308 result = Parser.ReadFrame(FrameBuffer);
310 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
312 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
313 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
314 result = RESULT_ENDOFFILE;
318 if ( Options.verbose_flag )
319 FrameBuffer.Dump(stderr);
321 if ( ASDCP_SUCCESS(result) )
323 split_buffer(PCM::CalcSampleSize(ADesc), FrameBuffer, L_FrameBuffer, R_FrameBuffer);
324 result = L_OutFile.Write(L_FrameBuffer.Data(), L_FrameBuffer.Size(), &write_count);
326 if ( ASDCP_SUCCESS(result) )
327 result = R_OutFile.Write(R_FrameBuffer.Data(), R_FrameBuffer.Size(), &write_count);
331 if ( result == RESULT_ENDOFFILE )
334 if ( ASDCP_SUCCESS(result) )
336 ADesc.ContainerDuration = duration;
337 Wav::SimpleWaveHeader WavHeader(ADesc);
340 if ( ASDCP_SUCCESS(result) )
341 result = R_OutFile.Seek();
343 if ( ASDCP_SUCCESS(result) )
344 result = WavHeader.WriteToFile(L_OutFile);
346 if ( ASDCP_SUCCESS(result) )
347 result = WavHeader.WriteToFile(R_OutFile);
357 main(int argc, const char** argv)
359 Result_t result = RESULT_OK;
360 CommandOptions Options(argc, argv);
362 if ( Options.help_flag )
368 if ( Options.error_flag )
371 if ( Options.version_flag )
374 if ( Options.info_flag )
375 result = wav_file_info(Options);
377 else if ( Options.create_flag )
378 result = split_wav_file(Options);
380 if ( result != RESULT_OK )
382 fputs("Program stopped on error.\n", stderr);
384 if ( result != RESULT_FAIL )
386 fputs(result, stderr);