2 Copyright (c) 2005-2006, 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
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 )
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-2006 John Hurst\n\n\
73 wavesplit 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);
82 usage(FILE* stream = stderr)
85 USAGE: %s [-i|-c <root-name> [-v]] <filename>\n\
88 -c <root-name> - Create a WAV file for each channel in the input file (default is two channel files)\n\
92 Read/Write Options:\n\
93 -f <frame-num> - Starting frame number, default 0\n\
94 -d <duration> - Number of frames to process, default all\n\
95 -v - Print extra info while processing\n\
97 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
98 o All option arguments must be separated from the option by whitespace.\n\
109 bool error_flag; // true if the given options are in error or not complete
110 bool create_flag; // true if the file create mode was selected
111 bool version_flag; // true if the version display option was selected
112 bool help_flag; // true if the help display option was selected
113 bool verbose_flag; // true for extra info during procesing
114 ui32_t start_frame; // frame number to begin processing
115 ui32_t duration; // number of frames to be processed
116 const char* file_root; // filename prefix for files written by the extract mode
117 const char* filename; // filename to be processed
119 CommandOptions(int argc, const char** argv) :
120 error_flag(true), create_flag(false),
121 version_flag(false), help_flag(false), start_frame(0),
122 duration(0xffffffff), file_root(0), filename(0)
124 for ( int i = 1; i < argc; i++ )
126 if ( argv[i][0] == '-' && isalpha(argv[i][1]) && argv[i][2] == 0 )
128 switch ( argv[i][1] )
130 case 'V': version_flag = true; break;
131 case 'h': help_flag = true; break;
133 TEST_SET_MAJOR_MODE(create_flag);
134 TEST_EXTRA_ARG(i, 'c');
139 TEST_EXTRA_ARG(i, 'f');
140 start_frame = atoi(argv[i]); // TODO: test for negative value, should use strtol()
144 TEST_EXTRA_ARG(i, 'd');
145 duration = atoi(argv[i]); // TODO: test for negative value, should use strtol()
149 fprintf(stderr, "Unrecognized option: %c\n", argv[i][1]);
157 fprintf(stderr, "Unexpected extra filename.\n");
165 if ( TEST_MAJOR_MODE() )
169 fputs("Input filename required.\n", stderr);
174 if ( ! TEST_MAJOR_MODE() && ! help_flag && ! version_flag )
176 fputs("No operation selected (use one of -(ic) or -h for help).\n", stderr);
188 split_buffer(ui32_t sample_size, PCM::FrameBuffer& FrameBuffer,
189 PCM::FrameBuffer& L_FrameBuffer, PCM::FrameBuffer& R_FrameBuffer)
191 assert((FrameBuffer.Size() % 2) == 0);
192 byte_t* p = FrameBuffer.Data();
193 byte_t* end_p = p + FrameBuffer.Size();
194 byte_t* lp = L_FrameBuffer.Data();
195 byte_t* rp = R_FrameBuffer.Data();
199 memcpy(lp, p, sample_size);
202 memcpy(rp, p, sample_size);
207 L_FrameBuffer.Size(L_FrameBuffer.Capacity());
208 R_FrameBuffer.Size(R_FrameBuffer.Capacity());
215 split_wav_file(CommandOptions& Options)
217 PCM::FrameBuffer FrameBuffer;
218 PCM::FrameBuffer L_FrameBuffer;
219 PCM::FrameBuffer R_FrameBuffer;
220 PCM::AudioDescriptor ADesc;
221 Rational PictureRate = EditRate_24;
222 PCM::WAVParser Parser;
224 // set up essence parser
225 Result_t result = Parser.OpenRead(Options.filename, PictureRate);
227 if ( ASDCP_SUCCESS(result) )
229 Parser.FillAudioDescriptor(ADesc);
231 ADesc.SampleRate = PictureRate;
232 ui32_t fb_size = PCM::CalcFrameBufferSize(ADesc);
233 assert((fb_size % 2) == 0);
234 FrameBuffer.Capacity(fb_size);
235 L_FrameBuffer.Capacity(fb_size/2);
236 R_FrameBuffer.Capacity(fb_size/2);
238 if ( Options.verbose_flag )
240 fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n", "24",
241 PCM::CalcSamplesPerFrame(ADesc));
242 fputs("AudioDescriptor:\n", stderr);
243 PCM::AudioDescriptorDump(ADesc);
246 ADesc.ChannelCount = 1;
249 // set up output files
250 Kumu::FileWriter L_OutFile;
251 Kumu::FileWriter R_OutFile;
253 if ( ASDCP_SUCCESS(result) )
256 snprintf(filename, 256, "%s_l.wav", Options.file_root);
257 result = L_OutFile.OpenWrite(filename);
259 if ( ASDCP_SUCCESS(result) )
261 snprintf(filename, 256, "%s_r.wav", Options.file_root);
262 result = R_OutFile.OpenWrite(filename);
267 if ( ASDCP_SUCCESS(result) )
269 Wav::SimpleWaveHeader WavHeader(ADesc);
270 result = WavHeader.WriteToFile(L_OutFile);
272 if ( ASDCP_SUCCESS(result) )
273 result = WavHeader.WriteToFile(R_OutFile);
276 if ( ASDCP_SUCCESS(result) )
278 ui32_t write_count = 0;
281 while ( ASDCP_SUCCESS(result) && (duration++ < Options.duration) )
283 result = Parser.ReadFrame(FrameBuffer);
285 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
287 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
288 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
289 result = RESULT_ENDOFFILE;
293 if ( Options.verbose_flag )
294 FrameBuffer.Dump(stderr);
296 if ( ASDCP_SUCCESS(result) )
298 split_buffer(PCM::CalcSampleSize(ADesc), FrameBuffer, L_FrameBuffer, R_FrameBuffer);
299 result = L_OutFile.Write(L_FrameBuffer.Data(), L_FrameBuffer.Size(), &write_count);
301 if ( ASDCP_SUCCESS(result) )
302 result = R_OutFile.Write(R_FrameBuffer.Data(), R_FrameBuffer.Size(), &write_count);
306 if ( result == RESULT_ENDOFFILE )
309 if ( ASDCP_SUCCESS(result) )
311 ADesc.ContainerDuration = duration;
312 Wav::SimpleWaveHeader WavHeader(ADesc);
315 if ( ASDCP_SUCCESS(result) )
316 result = R_OutFile.Seek();
318 if ( ASDCP_SUCCESS(result) )
319 result = WavHeader.WriteToFile(L_OutFile);
321 if ( ASDCP_SUCCESS(result) )
322 result = WavHeader.WriteToFile(R_OutFile);
332 main(int argc, const char** argv)
334 Result_t result = RESULT_OK;
335 CommandOptions Options(argc, argv);
337 if ( Options.help_flag )
343 if ( Options.error_flag )
346 if ( Options.version_flag )
349 if ( Options.create_flag )
350 result = split_wav_file(Options);
352 if ( result != RESULT_OK )
354 fputs("Program stopped on error.\n", stderr);
356 if ( result != RESULT_FAIL )
358 fputs(result, stderr);