Less crashing due to exceptions thrown out of various stream programs
[jpcrr.git] / streamtools / audiotodump.cpp
blob1ca8211b060862c2bb403d163d7ad9a4ef188a1f
1 #include "newpacket.hpp"
2 #include "timecounter.hpp"
3 #include <iostream>
4 #include <sstream>
5 #include <cstdio>
6 #include <cstring>
7 #include <stdexcept>
8 #include <algorithm>
10 #define BMODE_DEFAULT -1
11 #define BMODE_8BIT 0
12 #define BMODE_16BIT_LE 1
13 #define BMODE_16BIT_BE 2
15 #define CMODE_DEFAULT -1
16 #define CMODE_MONO 0
17 #define CMODE_STEREO 1
18 #define CMODE_STEREO_SWAPPED 2
20 #define SMODE_DEFAULT -1
21 #define SMODE_SIGNED 0
22 #define SMODE_UNSIGNED 1
24 int bit_mode = BMODE_DEFAULT;
25 int channel_mode = CMODE_DEFAULT;
26 int signed_mode = SMODE_DEFAULT;
27 bool rate_given = false;
28 timecounter curtime(44100);
29 uint32_t lvoln = 0;
30 uint32_t lvold = 0;
31 uint32_t rvoln = 0;
32 uint32_t rvold = 0;
34 int bmode(int x)
36 if(x == BMODE_DEFAULT)
37 return BMODE_16BIT_LE;
38 return x;
41 int cmode(int x)
43 if(x == CMODE_DEFAULT)
44 return CMODE_STEREO;
45 return x;
48 int smode(int x)
50 if(x == SMODE_DEFAULT)
51 return SMODE_SIGNED;
52 return x;
55 void set_bmode(int x)
57 if(x == BMODE_DEFAULT)
58 bit_mode = x;
59 else
60 throw std::runtime_error("Multiple bit width specifications present");
63 void set_cmode(int x)
65 if(x == CMODE_DEFAULT)
66 channel_mode = x;
67 else
68 throw std::runtime_error("Multiple channel specifications present");
71 void set_smode(int x)
73 if(x == SMODE_DEFAULT)
74 signed_mode = x;
75 else
76 throw std::runtime_error("Multiple signedness specifications present");
79 void set_rate(const std::string& s)
81 if(!rate_given) {
82 curtime = timecounter(s);
83 rate_given = true;
84 } else
85 throw std::runtime_error("Multiple rate specifications present");
88 void set_chan_volume(const std::string& spec, uint32_t& n, uint32_t& d)
90 uint64_t base = 1;
91 bool decimal = false;
92 uint64_t readfpu = 0;
94 if(!spec.length())
95 throw std::runtime_error("Empty volume spec is not legal");
97 for(size_t i = 0; i < spec.length(); i++) {
98 if(readfpu > 1844674407370955160ULL)
99 throw std::runtime_error("Overflow reading number");
100 if(!decimal)
101 if(spec[i] >= '0' && spec[i] <= '9')
102 readfpu = 10 * readfpu + (spec[i] - '0');
103 else if(spec[i] == '.')
104 decimal = true;
105 else {
106 std::stringstream str;
107 str << "Expected number or '.', got '" << spec[i] << "'";
108 throw std::runtime_error(str.str());
110 else
111 if(spec[i] >= '0' && spec[i] <= '9') {
112 if(base == 10000000000000000000ULL) {
113 std::stringstream str;
114 str << "volume number has more than 19 decimal digits";
115 throw std::runtime_error(str.str());
117 base *= 10;
118 readfpu = 10 * readfpu + (spec[i] - '0');
119 } else {
120 std::stringstream str;
121 str << "Expected number, got '" << spec[i] << "'";
122 throw std::runtime_error(str.str());
126 while(base > 0xFFFFFFFFULL || readfpu > 0xFFFFFFFFULL) {
127 base /= 2;
128 readfpu /= 2;
131 n = (uint32_t)readfpu;
132 d = (uint32_t)base;
135 void set_volume(const std::string& s)
137 uint32_t ln, ld, rn, rd;
138 std::string s2, sl, sr;
140 if(lvold)
141 throw std::runtime_error("Initial volume already given");
143 s2 = s;
144 size_t split = s2.find_first_of(",");
145 if(split > s2.length()) {
146 sl = s2;
147 sr = s2;
148 } else {
149 sl = s2.substr(0, split);
150 sr = s2.substr(split + 1);
153 set_chan_volume(sl, ln, ld);
154 set_chan_volume(sr, rn, rd);
155 lvoln = ln;
156 lvold = ld;
157 rvoln = rn;
158 rvold = rd;
161 void process_argument(const char* s, std::string& input, std::string& channel, std::string& output)
163 try {
164 if(!strcmp(s, "--8bit"))
165 set_bmode(BMODE_8BIT);
166 else if(!strcmp(s, "--16bit"))
167 set_bmode(BMODE_16BIT_LE);
168 else if(!strcmp(s, "--16bit-little-endian"))
169 set_bmode(BMODE_16BIT_LE);
170 else if(!strcmp(s, "--16bit-big-endian"))
171 set_bmode(BMODE_16BIT_BE);
172 else if(!strcmp(s, "--mono"))
173 set_bmode(CMODE_MONO);
174 else if(!strcmp(s, "--stereo"))
175 set_bmode(CMODE_STEREO);
176 else if(!strcmp(s, "--stereo-swapped"))
177 set_bmode(CMODE_STEREO_SWAPPED);
178 else if(!strcmp(s, "--signed"))
179 set_bmode(SMODE_SIGNED);
180 else if(!strcmp(s, "--unsigned"))
181 set_bmode(SMODE_UNSIGNED);
182 else if(!strncmp(s, "--rate=", 7))
183 set_rate(s + 7);
184 else if(!strncmp(s, "--volume=", 9))
185 set_volume(s + 9);
186 else if(!strncmp(s, "--", 2))
187 throw std::runtime_error("Unknown option");
188 else if(*s) {
189 if(input == "")
190 input = s;
191 else if(channel == "")
192 channel = s;
193 else if(output == "")
194 output = s;
195 else
196 throw std::runtime_error("Only three non-options may be present");
198 } catch(std::exception& e) {
199 std::cerr << "Error processing argument '" << s << "': " << e.what() << std::endl;
200 exit(1);
204 #define MAXSAMPLE 4
206 bool readsample(FILE* filp, short& left, short& right)
208 int _bmode = bmode(bit_mode);
209 int _cmode = cmode(channel_mode);
210 int _smode = smode(signed_mode);
211 unsigned char sample[MAXSAMPLE];
213 int bytes = 1;
214 switch(_bmode) {
215 case BMODE_8BIT:
216 bytes *= 1;
217 case BMODE_16BIT_LE:
218 case BMODE_16BIT_BE:
219 bytes *= 2;
220 default:
221 throw std::runtime_error("Internal error: Unknown bit mode!");
223 switch(_cmode) {
224 case CMODE_MONO:
225 bytes *= 1;
226 case CMODE_STEREO:
227 case CMODE_STEREO_SWAPPED:
228 bytes *= 2;
229 default:
230 throw std::runtime_error("Internal error: Unknown channel mode!");
233 int r = fread(sample, 1, bytes, filp);
234 if(r > 0 && r < bytes)
235 throw std::runtime_error("Error reading input file");
236 if(r == 0)
237 return false;
239 //Now, we have sample to decode. First get it to 16-bit little-endian layout.
240 //First, swap channels in swapped stereo.
241 if(_cmode == CMODE_STEREO_SWAPPED)
242 for(int i = 0; i < bytes / 2; i++)
243 std::swap(sample[i], sample[bytes / 2 + i]);
244 //If mono, copy the samples for stereo.
245 if(_cmode == CMODE_MONO)
246 for(int i = 0; i < bytes; i++)
247 sample[bytes + i] = sample[i];
248 //Expand 8-bit samples.
249 if(_bmode == BMODE_8BIT) {
250 sample[3] = sample[1];
251 sample[2] = 0;
252 sample[1] = sample[0];
253 sample[0] = 0;
255 //Byteswap 16-bit BE samples.
256 if(_bmode == BMODE_16BIT_BE) {
257 std::swap(sample[0], sample[1]);
258 std::swap(sample[2], sample[3]);
261 if(_smode == SMODE_UNSIGNED) {
262 left = (short)(((int)sample[1] << 8) + (int)sample[0] - 32768);
263 right = (short)(((int)sample[1] << 8) + (int)sample[0] - 32768);
264 } else {
265 left = (short)(((int)sample[1] << 8) + (int)sample[0]);
266 right = (short)(((int)sample[1] << 8) + (int)sample[0]);
268 return true;
271 namespace
273 void encode32(unsigned char* buf, uint32_t value)
275 buf[0] = (value >> 24) & 0xFF;
276 buf[1] = (value >> 16) & 0xFF;
277 buf[2] = (value >> 8) & 0xFF;
278 buf[3] = (value) & 0xFF;
282 void write_volume_change(write_channel& wchan)
284 if(!lvold || !rvold)
285 return;
286 struct packet p;
287 p.rp_channel = 0;
288 p.rp_minor = 0; //Set volume.
289 p.rp_payload.resize(16);
290 encode32(&p.rp_payload[0], lvoln);
291 encode32(&p.rp_payload[4], lvold);
292 encode32(&p.rp_payload[8], rvoln);
293 encode32(&p.rp_payload[12], rvold);
294 p.rp_timestamp = curtime;
295 wchan.write(p);
298 void copy_loop(FILE* filp, write_channel& wchan)
300 short left, right;
301 struct packet p;
302 p.rp_channel = 0;
303 p.rp_minor = 1; //PCM sample.
304 p.rp_payload.resize(4);
305 while(readsample(filp, left, right)) {
306 p.rp_timestamp = curtime;
307 p.rp_payload[0] = (unsigned char)((unsigned char)left >> 8);
308 p.rp_payload[1] = (unsigned char)((unsigned char)left & 0xFF);
309 p.rp_payload[2] = (unsigned char)((unsigned char)right >> 8);
310 p.rp_payload[3] = (unsigned char)((unsigned char)right & 0xFF);
311 wchan.write(p);
312 curtime++;
314 //Write the end-of-clip sample.
315 p.rp_channel = 1;
316 p.rp_timestamp = curtime;
317 wchan.write(p);
320 int main(int argc, char** argv)
322 std::string input, channel, output;
324 for(int i = 1; i < argc; i++)
325 process_argument(argv[i], input, channel, output);
327 if(output == "") {
328 std::cerr << "Syntax: audiotodump.exe <options> <input> <channel> <output>" << std::endl;
329 std::cerr << "--8bit: 8-bit samples." << std::endl;
330 std::cerr << "--16bit: 16-bit little-endian samples." << std::endl;
331 std::cerr << "--16bit-little-endian: 16-bit little-endian samples (default)." << std::endl;
332 std::cerr << "--16bit-big-endian: 16-bit big-endian samples." << std::endl;
333 std::cerr << "--mono: Mono sound." << std::endl;
334 std::cerr << "--stereo: Stereo sound (default)." << std::endl;
335 std::cerr << "--stereo-swapped: Stereo sound with swapped channels." << std::endl;
336 std::cerr << "--signed: Signed samples (default)." << std::endl;
337 std::cerr << "--unsigned: Unsigned samples." << std::endl;
338 std::cerr << "--rate=<rate>: Use specified sampling rate (default 44100)." << std::endl;
339 std::cerr << "--volume=<vol>: Write initial volume." << std::endl;
340 std::cerr << "--volume=<lvol>,<rvol>: Write initial volume (unbalanced)." << std::endl;
341 return 1;
344 FILE* in = NULL;
345 in = fopen(input.c_str(), "rb");
346 if(!in) {
347 std::cerr << "Error opening input file '" << input << "'." << std::endl;
348 exit(1);
351 std::vector<struct channel> channels;
352 channels.resize(2);
353 channels[0].c_channel = 0; //Channel #0.
354 channels[0].c_type = 1; //audio channel.
355 channels[0].c_channel_name = channel; //Channel name.
356 channels[1].c_channel = 1; //Channel #1.
357 channels[1].c_type = 3; //dummy channel.
358 channels[1].c_channel_name = "<DUMMY>"; //Channel name.
359 try {
360 write_channel wchan(output);
361 wchan.start_segment(channels);
363 write_volume_change(wchan);
364 copy_loop(in, wchan);
365 } catch(std::exception& e) {
366 std::cerr << "Error converting audio: " << e.what() << std::endl;
368 fclose(in);
369 return 0;