1/*
2 *
3 *  Bluetooth low-complexity, subband codec (SBC) encoder
4 *
5 *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
6 *
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to the Free Software
20 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <stdio.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <stdint.h>
34#include <string.h>
35#include <getopt.h>
36#include <sys/stat.h>
37
38#include "sbc.h"
39#include "formats.h"
40
41static int verbose = 0;
42
43#define BUF_SIZE 32768
44static unsigned char input[BUF_SIZE], output[BUF_SIZE + BUF_SIZE / 4];
45
46static void encode(char *filename, int subbands, int bitpool, int joint,
47					int dualchannel, int snr, int blocks)
48{
49	struct au_header au_hdr;
50	sbc_t sbc;
51	int fd, size, srate, codesize, nframes;
52	size_t encoded;
53	ssize_t len;
54
55	if (sizeof(au_hdr) != 24) {
56		/* Sanity check just in case */
57		fprintf(stderr, "FIXME: sizeof(au_hdr) != 24\n");
58		return;
59	}
60
61	if (strcmp(filename, "-")) {
62		fd = open(filename, O_RDONLY);
63		if (fd < 0) {
64			fprintf(stderr, "Can't open file %s: %s\n",
65						filename, strerror(errno));
66			return;
67		}
68	} else
69		fd = fileno(stdin);
70
71	len = read(fd, &au_hdr, sizeof(au_hdr));
72	if (len < (ssize_t) sizeof(au_hdr)) {
73		if (fd > fileno(stderr))
74			fprintf(stderr, "Can't read header from file %s: %s\n",
75						filename, strerror(errno));
76		else
77			perror("Can't read audio header");
78		goto done;
79	}
80
81	if (au_hdr.magic != AU_MAGIC ||
82			BE_INT(au_hdr.hdr_size) > 128 ||
83			BE_INT(au_hdr.hdr_size) < sizeof(au_hdr) ||
84			BE_INT(au_hdr.encoding) != AU_FMT_LIN16) {
85		fprintf(stderr, "Not in Sun/NeXT audio S16_BE format\n");
86		goto done;
87	}
88
89	sbc_init(&sbc, 0L);
90
91	switch (BE_INT(au_hdr.sample_rate)) {
92	case 16000:
93		sbc.frequency = SBC_FREQ_16000;
94		break;
95	case 32000:
96		sbc.frequency = SBC_FREQ_32000;
97		break;
98	case 44100:
99		sbc.frequency = SBC_FREQ_44100;
100		break;
101	case 48000:
102		sbc.frequency = SBC_FREQ_48000;
103		break;
104	}
105
106	srate = BE_INT(au_hdr.sample_rate);
107
108	sbc.subbands = subbands == 4 ? SBC_SB_4 : SBC_SB_8;
109
110	if (BE_INT(au_hdr.channels) == 1) {
111		sbc.mode = SBC_MODE_MONO;
112		if (joint || dualchannel) {
113			fprintf(stderr, "Audio is mono but joint or "
114				"dualchannel mode has been specified\n");
115			goto done;
116		}
117	} else if (joint && !dualchannel)
118		sbc.mode = SBC_MODE_JOINT_STEREO;
119	else if (!joint && dualchannel)
120		sbc.mode = SBC_MODE_DUAL_CHANNEL;
121	else if (!joint && !dualchannel)
122		sbc.mode = SBC_MODE_STEREO;
123	else {
124		fprintf(stderr, "Both joint and dualchannel mode have been "
125								"specified\n");
126		goto done;
127	}
128
129	sbc.endian = SBC_BE;
130	/* Skip extra bytes of the header if any */
131	if (read(fd, input, BE_INT(au_hdr.hdr_size) - len) < 0)
132		goto done;
133
134	sbc.bitpool = bitpool;
135	sbc.allocation = snr ? SBC_AM_SNR : SBC_AM_LOUDNESS;
136	sbc.blocks = blocks == 4 ? SBC_BLK_4 :
137			blocks == 8 ? SBC_BLK_8 :
138				blocks == 12 ? SBC_BLK_12 : SBC_BLK_16;
139
140	if (verbose) {
141		fprintf(stderr, "encoding %s with rate %d, %d blocks, "
142			"%d subbands, %d bits, allocation method %s, "
143							"and mode %s\n",
144			filename, srate, blocks, subbands, bitpool,
145			sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS",
146			sbc.mode == SBC_MODE_MONO ? "MONO" :
147					sbc.mode == SBC_MODE_STEREO ?
148						"STEREO" : "JOINTSTEREO");
149	}
150
151	codesize = sbc_get_codesize(&sbc);
152	nframes = sizeof(input) / codesize;
153	while (1) {
154		unsigned char *inp, *outp;
155		/* read data for up to 'nframes' frames of input data */
156		size = read(fd, input, codesize * nframes);
157		if (size < 0) {
158			/* Something really bad happened */
159			perror("Can't read audio data");
160			break;
161		}
162		if (size < codesize) {
163			/* Not enough data for encoding even a single frame */
164			break;
165		}
166		/* encode all the data from the input buffer in a loop */
167		inp = input;
168		outp = output;
169		while (size >= codesize) {
170			len = sbc_encode(&sbc, inp, codesize,
171				outp, sizeof(output) - (outp - output),
172				&encoded);
173			if (len != codesize || encoded <= 0) {
174				fprintf(stderr,
175					"sbc_encode fail, len=%zd, encoded=%lu\n",
176					len, (unsigned long) encoded);
177				break;
178			}
179			size -= len;
180			inp += len;
181			outp += encoded;
182		}
183		len = write(fileno(stdout), output, outp - output);
184		if (len != outp - output) {
185			perror("Can't write SBC output");
186			break;
187		}
188		if (size != 0) {
189			/*
190			 * sbc_encode failure has been detected earlier or end
191			 * of file reached (have trailing partial data which is
192			 * insufficient to encode SBC frame)
193			 */
194			break;
195		}
196	}
197
198	sbc_finish(&sbc);
199
200done:
201	if (fd > fileno(stderr))
202		close(fd);
203}
204
205static void usage(void)
206{
207	printf("SBC encoder utility ver %s\n", VERSION);
208	printf("Copyright (c) 2004-2009  Marcel Holtmann\n\n");
209
210	printf("Usage:\n"
211		"\tsbcenc [options] file(s)\n"
212		"\n");
213
214	printf("Options:\n"
215		"\t-h, --help           Display help\n"
216		"\t-v, --verbose        Verbose mode\n"
217		"\t-s, --subbands       Number of subbands to use (4 or 8)\n"
218		"\t-b, --bitpool        Bitpool value (default is 32)\n"
219		"\t-j, --joint          Joint stereo\n"
220		"\t-d, --dualchannel    Dual channel\n"
221		"\t-S, --snr            Use SNR mode (default is loudness)\n"
222		"\t-B, --blocks         Number of blocks (4, 8, 12 or 16)\n"
223		"\n");
224}
225
226static struct option main_options[] = {
227	{ "help",	0, 0, 'h' },
228	{ "verbose",	0, 0, 'v' },
229	{ "subbands",	1, 0, 's' },
230	{ "bitpool",	1, 0, 'b' },
231	{ "joint",	0, 0, 'j' },
232	{ "dualchannel",0, 0, 'd' },
233	{ "snr",	0, 0, 'S' },
234	{ "blocks",	1, 0, 'B' },
235	{ 0, 0, 0, 0 }
236};
237
238int main(int argc, char *argv[])
239{
240	int i, opt, subbands = 8, bitpool = 32, joint = 0, dualchannel = 0;
241	int snr = 0, blocks = 16;
242
243	while ((opt = getopt_long(argc, argv, "+hvs:b:jdSB:",
244						main_options, NULL)) != -1) {
245		switch(opt) {
246		case 'h':
247			usage();
248			exit(0);
249
250		case 'v':
251			verbose = 1;
252			break;
253
254		case 's':
255			subbands = atoi(optarg);
256			if (subbands != 8 && subbands != 4) {
257				fprintf(stderr, "Invalid subbands\n");
258				exit(1);
259			}
260			break;
261
262		case 'b':
263			bitpool = atoi(optarg);
264			break;
265
266		case 'j':
267			joint = 1;
268			break;
269
270		case 'd':
271			dualchannel = 1;
272			break;
273
274		case 'S':
275			snr = 1;
276			break;
277
278		case 'B':
279			blocks = atoi(optarg);
280			if (blocks != 16 && blocks != 12 &&
281						blocks != 8 && blocks != 4) {
282				fprintf(stderr, "Invalid blocks\n");
283				exit(1);
284			}
285			break;
286
287		default:
288			usage();
289			exit(1);
290		}
291	}
292
293	argc -= optind;
294	argv += optind;
295	optind = 0;
296
297	if (argc < 1) {
298		usage();
299		exit(1);
300	}
301
302	for (i = 0; i < argc; i++)
303		encode(argv[i], subbands, bitpool, joint, dualchannel,
304								snr, blocks);
305
306	return 0;
307}
308