JPC-RR r11.7
[jpcrr.git] / streamtools / audiotodump.cpp
blobbdd2534dd1094f66634a3f6b27e7a265b6fad4e4
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(bit_mode == 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(channel_mode == 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(signed_mode == 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 break;
218 case BMODE_16BIT_LE:
219 case BMODE_16BIT_BE:
220 bytes *= 2;
221 break;
222 default:
223 throw std::runtime_error("Internal error: Unknown bit mode!");
225 switch(_cmode) {
226 case CMODE_MONO:
227 bytes *= 1;
228 break;
229 case CMODE_STEREO:
230 case CMODE_STEREO_SWAPPED:
231 bytes *= 2;
232 break;
233 default:
234 throw std::runtime_error("Internal error: Unknown channel mode!");
237 int r = fread(sample, 1, bytes, filp);
238 if(r > 0 && r < bytes)
239 throw std::runtime_error("Error reading input file");
240 if(r == 0)
241 return false;
243 //Now, we have sample to decode. First get it to 16-bit little-endian layout.
244 //First, swap channels in swapped stereo.
245 if(_cmode == CMODE_STEREO_SWAPPED)
246 for(int i = 0; i < bytes / 2; i++)
247 std::swap(sample[i], sample[bytes / 2 + i]);
248 //If mono, copy the samples for stereo.
249 if(_cmode == CMODE_MONO)
250 for(int i = 0; i < bytes; i++)
251 sample[bytes + i] = sample[i];
252 //Expand 8-bit samples.
253 if(_bmode == BMODE_8BIT) {
254 sample[3] = sample[1];
255 sample[2] = 0;
256 sample[1] = sample[0];
257 sample[0] = 0;
259 //Byteswap 16-bit BE samples.
260 if(_bmode == BMODE_16BIT_BE) {
261 std::swap(sample[0], sample[1]);
262 std::swap(sample[2], sample[3]);
265 if(_smode == SMODE_UNSIGNED) {
266 left = (short)(((int)sample[1] << 8) + (int)sample[0] - 32768);
267 right = (short)(((int)sample[1] << 8) + (int)sample[0] - 32768);
268 } else {
269 left = (short)(((int)sample[1] << 8) + (int)sample[0]);
270 right = (short)(((int)sample[1] << 8) + (int)sample[0]);
272 return true;
275 namespace
277 void encode32(unsigned char* buf, uint32_t value)
279 buf[0] = (value >> 24) & 0xFF;
280 buf[1] = (value >> 16) & 0xFF;
281 buf[2] = (value >> 8) & 0xFF;
282 buf[3] = (value) & 0xFF;
286 void write_volume_change(write_channel& wchan)
288 if(!lvold || !rvold)
289 return;
290 struct packet p;
291 p.rp_channel = 0;
292 p.rp_minor = 0; //Set volume.
293 p.rp_payload.resize(16);
294 encode32(&p.rp_payload[0], lvoln);
295 encode32(&p.rp_payload[4], lvold);
296 encode32(&p.rp_payload[8], rvoln);
297 encode32(&p.rp_payload[12], rvold);
298 p.rp_timestamp = curtime;
299 wchan.write(p);
302 void copy_loop(FILE* filp, write_channel& wchan)
304 short left, right;
305 struct packet p;
306 p.rp_channel = 0;
307 p.rp_minor = 1; //PCM sample.
308 p.rp_payload.resize(4);
309 while(readsample(filp, left, right)) {
310 p.rp_timestamp = curtime;
311 p.rp_payload[0] = (unsigned char)((unsigned char)left >> 8);
312 p.rp_payload[1] = (unsigned char)((unsigned char)left & 0xFF);
313 p.rp_payload[2] = (unsigned char)((unsigned char)right >> 8);
314 p.rp_payload[3] = (unsigned char)((unsigned char)right & 0xFF);
315 wchan.write(p);
316 curtime++;
318 //Write the end-of-clip sample.
319 p.rp_channel = 1;
320 p.rp_timestamp = curtime;
321 wchan.write(p);
324 int real_main(int argc, char** argv)
326 std::string input, channel, output;
328 for(int i = 1; i < argc; i++)
329 process_argument(argv[i], input, channel, output);
331 if(output == "") {
332 std::cerr << "Syntax: audiotodump.exe <options> <input> <channel> <output>" << std::endl;
333 std::cerr << "--8bit: 8-bit samples." << std::endl;
334 std::cerr << "--16bit: 16-bit little-endian samples." << std::endl;
335 std::cerr << "--16bit-little-endian: 16-bit little-endian samples (default)." << std::endl;
336 std::cerr << "--16bit-big-endian: 16-bit big-endian samples." << std::endl;
337 std::cerr << "--mono: Mono sound." << std::endl;
338 std::cerr << "--stereo: Stereo sound (default)." << std::endl;
339 std::cerr << "--stereo-swapped: Stereo sound with swapped channels." << std::endl;
340 std::cerr << "--signed: Signed samples (default)." << std::endl;
341 std::cerr << "--unsigned: Unsigned samples." << std::endl;
342 std::cerr << "--rate=<rate>: Use specified sampling rate (default 44100)." << std::endl;
343 std::cerr << "--volume=<vol>: Write initial volume." << std::endl;
344 std::cerr << "--volume=<lvol>,<rvol>: Write initial volume (unbalanced)." << std::endl;
345 return 1;
348 FILE* in = NULL;
349 in = fopen(input.c_str(), "rb");
350 if(!in) {
351 std::cerr << "Error opening input file '" << input << "'." << std::endl;
352 exit(1);
355 std::vector<struct channel> channels;
356 channels.resize(2);
357 channels[0].c_channel = 0; //Channel #0.
358 channels[0].c_type = 1; //audio channel.
359 channels[0].c_channel_name = channel; //Channel name.
360 channels[1].c_channel = 1; //Channel #1.
361 channels[1].c_type = 3; //dummy channel.
362 channels[1].c_channel_name = "<DUMMY>"; //Channel name.
363 try {
364 write_channel wchan(output);
365 wchan.start_segment(channels);
367 write_volume_change(wchan);
368 copy_loop(in, wchan);
369 } catch(std::exception& e) {
370 std::cerr << "Error converting audio: " << e.what() << std::endl;
372 fclose(in);
373 return 0;