ReadFileIntoString() modified to return OK when the file is empty
[asdcplib.git] / src / blackwave.cpp
1 /*
2 Copyright (c) 2005-2012, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27 /*! \file    wavsplit.cpp
28     \version $Id$
29     \brief   Black WAV file generator
30 */
31
32 #include "Wav.h"
33 #include <assert.h>
34
35 using namespace ASDCP;
36
37 //------------------------------------------------------------------------------------------
38 //
39 // command line option parser class
40
41 static const char* PROGRAM_NAME = "blackwave";    // program name for messages
42
43 // Macros used to test command option data state.
44
45 // Increment the iterator, test for an additional non-option command line argument.
46 // Causes the caller to return if there are no remaining arguments or if the next
47 // argument begins with '-'.
48 #define TEST_EXTRA_ARG(i,c)    if ( ++i >= argc || argv[(i)][0] == '-' ) \
49                                  { \
50                                    fprintf(stderr, "Argument not found for option %c.\n", (c)); \
51                                    return; \
52                                  }
53 //
54 void
55 banner(FILE* stream = stderr)
56 {
57   fprintf(stream, "\n\
58 %s (asdcplib %s)\n\n\
59 Copyright (c) 2005-2012 John Hurst\n\n\
60 %s is part of asdcplib.\n\
61 asdcplib may be copied only under the terms of the license found at\n\
62 the top of every file in the asdcplib distribution kit.\n\n\
63 Specify the -h (help) option for further information about %s\n\n",
64           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME, PROGRAM_NAME);
65 }
66
67 //
68 void
69 usage(FILE* stream = stderr)
70 {
71   fprintf(stream, "\
72 USAGE: %s [-v|-h[-d]] <filename>\n\
73 \n\
74   -V              - Show version\n\
75   -h              - Show help\n\
76   -d <duration>   - Number of edit units to process, default 1440\n\
77   -9              - Make a 96 kHz file (default 48 kHz)\n\
78 \n\
79 Other Options:\n\
80   -v              - Verbose, show extra detail during run\n\
81 \n\
82   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
83          o All option arguments must be separated from the option by whitespace.\n\
84 \n", PROGRAM_NAME);
85 }
86
87 //
88 //
89 class CommandOptions
90 {
91   CommandOptions();
92
93 public:
94   bool   error_flag;     // true if the given options are in error or not complete
95   bool   verbose_flag;   // true if the verbose option was selected
96   bool   version_flag;   // true if the version display option was selected
97   bool   help_flag;      // true if the help display option was selected
98   bool   s96_flag;       // true if the samples should be at 96 kHz
99   ui32_t duration;       // number of frames to be processed
100   const char* filename;  // filename prefix for files written by the extract mode
101
102   CommandOptions(int argc, const char** argv) :
103     error_flag(true), verbose_flag(false), version_flag(false), help_flag(false), s96_flag(false),
104     duration(1440), filename(0)
105   {
106     for ( int i = 1; i < argc; i++ )
107       {
108         if ( argv[i][0] == '-' && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) && argv[i][2] == 0 )
109           {
110             switch ( argv[i][1] )
111               {
112               case 'V': version_flag = true; break;
113               case 'h': help_flag = true; break;
114               case 'v': verbose_flag = true; break;
115
116               case 'd':
117                 TEST_EXTRA_ARG(i, 'd');
118                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
119                 break;
120
121               case '9':
122                 s96_flag = true;
123                 break;
124
125               default:
126                 fprintf(stderr, "Unrecognized option: %c\n", argv[i][1]);
127                 return;
128               }
129           }
130         else
131           {
132             if ( filename )
133               {
134                 fprintf(stderr, "Unexpected extra filename.\n");
135                 return;
136               }
137
138             filename = argv[i];
139           }
140       }
141
142     if ( filename == 0 )
143       {
144         fputs("Output filename required.\n", stderr);
145         return;
146       }
147
148     error_flag = false;
149   }
150 };
151
152
153 // 
154 //
155 Result_t
156 make_black_wav_file(CommandOptions& Options)
157 {
158   PCM::FrameBuffer FrameBuffer;
159   PCM::AudioDescriptor ADesc;
160
161   ADesc.EditRate = Rational(24,1);
162   ADesc.AudioSamplingRate = Options.s96_flag ? ASDCP::SampleRate_96k : ASDCP::SampleRate_48k;
163   ADesc.Locked = 0;
164   ADesc.ChannelCount = 1;
165   ADesc.QuantizationBits = 24;
166   ADesc.BlockAlign = 3;
167   ADesc.AvgBps = ADesc.BlockAlign * ADesc.AudioSamplingRate.Quotient();
168   ADesc.LinkedTrackID = 1;
169   ADesc.ContainerDuration = Options.duration;
170
171   // fill the frame buffer with a frame (2000 samples) of black
172   FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
173   memset(FrameBuffer.Data(), 0, FrameBuffer.Capacity());
174   FrameBuffer.Size(FrameBuffer.Capacity());
175
176   if ( Options.verbose_flag )
177     {
178       fprintf(stderr, "%s kHz PCM Audio, 24 fps (%u spf)\n",
179               (Options.s96_flag?"96":"48"), PCM::CalcSamplesPerFrame(ADesc));
180       fputs("AudioDescriptor:\n", stderr);
181       PCM::AudioDescriptorDump(ADesc);
182     }
183
184   // set up output file
185   Kumu::FileWriter OutFile;
186   Result_t result = OutFile.OpenWrite(Options.filename);
187
188   if ( ASDCP_SUCCESS(result) )
189     {
190        RF64::SimpleRF64Header WavHeader(ADesc);
191        result = WavHeader.WriteToFile(OutFile);
192     }
193
194   if ( ASDCP_SUCCESS(result) )
195     {
196       ui32_t write_count = 0;
197       ui32_t duration = 0;
198
199       while ( ASDCP_SUCCESS(result) && (duration++ < Options.duration) )
200         {
201           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
202         }
203
204       if ( result == RESULT_ENDOFFILE )
205         result = RESULT_OK;
206     }
207
208   return RESULT_OK;
209 }
210
211
212 //
213 int
214 main(int argc, const char** argv)
215 {
216   Result_t result = RESULT_OK;
217   CommandOptions Options(argc, argv);
218
219   if ( Options.help_flag )
220     {
221       usage();
222       return 0;
223     }
224
225   if ( Options.error_flag )
226     return 3;
227
228   if ( Options.version_flag )
229     banner();
230
231   else
232     result = make_black_wav_file(Options);
233
234   if ( result != RESULT_OK )
235     {
236       fputs("Program stopped on error.\n", stderr);
237
238       if ( result != RESULT_FAIL )
239         {
240           fputs(result, stderr);
241           fputc('\n', stderr);
242         }
243
244       return 1;
245     }
246
247   return 0;
248 }
249
250
251 //