1 /*
2 	Audio File Library
3 
4 	Copyright (C) 1998, 2011-2012, Michael Pruett <michael@68k.org>
5 	Copyright (C) 2001, Silicon Graphics, Inc.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 2 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	You should have received a copy of the GNU General Public License along
18 	with this program; if not, write to the Free Software Foundation, Inc.,
19 	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21 
22 /*
23 	sfconvert is a program which can convert various parameters of
24 	sound files.
25 */
26 
27 #include "config.h"
28 
29 #ifdef __USE_SGI_HEADERS__
30 #include <dmedia/audiofile.h>
31 #else
32 #include <audiofile.h>
33 #endif
34 
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "printinfo.h"
42 
43 void printversion (void);
44 void printusage (void);
45 void usageerror (void);
46 bool copyaudiodata (AFfilehandle infile, AFfilehandle outfile, int trackid);
47 
main(int argc,char ** argv)48 int main (int argc, char **argv)
49 {
50 	if (argc == 2)
51 	{
52 		if (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v"))
53 		{
54 			printversion();
55 			exit(EXIT_SUCCESS);
56 		}
57 
58 		if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))
59 		{
60 			printusage();
61 			exit(EXIT_SUCCESS);
62 		}
63 	}
64 
65 	if (argc < 3)
66 		usageerror();
67 
68 	const char *inFileName = argv[1];
69 	const char *outFileName = argv[2];
70 
71 	int outFileFormat = AF_FILE_UNKNOWN;
72 	int outSampleFormat = -1, outSampleWidth = -1, outChannelCount = -1;
73 	int outCompression = AF_COMPRESSION_NONE;
74 	double outMaxAmp = 1.0;
75 
76 	int i = 3;
77 
78 	while (i < argc)
79 	{
80 		if (!strcmp(argv[i], "format"))
81 		{
82 			if (i + 1 >= argc)
83 				usageerror();
84 
85 			if (!strcmp(argv[i+1], "aiff"))
86 				outFileFormat = AF_FILE_AIFF;
87 			else if (!strcmp(argv[i+1], "aifc"))
88 				outFileFormat = AF_FILE_AIFFC;
89 			else if (!strcmp(argv[i+1], "wave"))
90 				outFileFormat = AF_FILE_WAVE;
91 			else if (!strcmp(argv[i+1], "next"))
92 				outFileFormat = AF_FILE_NEXTSND;
93 			else if (!strcmp(argv[i+1], "bics"))
94 				outFileFormat = AF_FILE_BICSF;
95 			else if (!strcmp(argv[i+1], "smp"))
96 				outFileFormat = AF_FILE_SAMPLEVISION;
97 			else if (!strcmp(argv[i+1], "voc"))
98 				outFileFormat = AF_FILE_VOC;
99 			else if (!strcmp(argv[i+1], "nist"))
100 				outFileFormat = AF_FILE_NIST_SPHERE;
101 			else if (!strcmp(argv[i+1], "caf"))
102 				outFileFormat = AF_FILE_CAF;
103 			else if (!strcmp(argv[i+1], "flac"))
104 				outFileFormat = AF_FILE_FLAC;
105 			else
106 			{
107 				fprintf(stderr, "sfconvert: Unknown format %s.\n", argv[i+1]);
108 				exit(EXIT_FAILURE);
109 			}
110 
111 			// Increment for argument.
112 			i++;
113 		}
114 		else if (!strcmp(argv[i], "channels"))
115 		{
116 			if (i + 1 >= argc)
117 				usageerror();
118 
119 			outChannelCount = atoi(argv[i+1]);
120 			if (outChannelCount < 1)
121 				usageerror();
122 
123 			// Increment for argument.
124 			i++;
125 		}
126 		else if (!strcmp(argv[i], "float"))
127 		{
128 			if (i + 1 >= argc)
129 				usageerror();
130 
131 			outSampleFormat = AF_SAMPFMT_FLOAT;
132 			outSampleWidth = 32;
133 			outMaxAmp = atof(argv[i+1]);
134 
135 			// outMaxAmp is currently unused.
136 			(void) outMaxAmp;
137 
138 			// Increment for argument.
139 			i++;
140 		}
141 		else if (!strcmp(argv[i], "integer"))
142 		{
143 			if (i + 2 >= argc)
144 				usageerror();
145 
146 			outSampleWidth = atoi(argv[i+1]);
147 			if (outSampleWidth < 1 || outSampleWidth > 32)
148 				usageerror();
149 
150 			if (!strcmp(argv[i+2], "2scomp"))
151 				outSampleFormat = AF_SAMPFMT_TWOSCOMP;
152 			else if (!strcmp(argv[i+2], "unsigned"))
153 				outSampleFormat = AF_SAMPFMT_UNSIGNED;
154 			else
155 				usageerror();
156 
157 			// Increment for arguments.
158 			i += 2;
159 		}
160 		else if (!strcmp(argv[i], "compression"))
161 		{
162 			if (i + 1 >= argc)
163 				usageerror();
164 
165 			if (!strcmp(argv[i+1], "none"))
166 				outCompression = AF_COMPRESSION_NONE;
167 			else if (!strcmp(argv[i+1], "ulaw"))
168 				outCompression = AF_COMPRESSION_G711_ULAW;
169 			else if (!strcmp(argv[i+1], "alaw"))
170 				outCompression = AF_COMPRESSION_G711_ALAW;
171 			else if (!strcmp(argv[i+1], "ima"))
172 				outCompression = AF_COMPRESSION_IMA;
173 			else if (!strcmp(argv[i+1], "msadpcm"))
174 				outCompression = AF_COMPRESSION_MS_ADPCM;
175 			else if (!strcmp(argv[i+1], "flac"))
176 				outCompression = AF_COMPRESSION_FLAC;
177 			else if (!strcmp(argv[i+1], "alac"))
178 				outCompression = AF_COMPRESSION_ALAC;
179 			else
180 			{
181 				fprintf(stderr, "sfconvert: Unknown compression format %s.\n", argv[i+1]);
182 				exit(EXIT_FAILURE);
183 			}
184 
185 			i++;
186 		}
187 		else
188 		{
189 			printf("Unrecognized command %s\n", argv[i]);
190 		}
191 
192 		i++;
193 	}
194 
195 	AFfilehandle inFile = afOpenFile(inFileName, "r", AF_NULL_FILESETUP);
196 	if (!inFile)
197 	{
198 		printf("Could not open file '%s' for reading.\n", inFileName);
199 		return EXIT_FAILURE;
200 	}
201 
202 	// Get audio format parameters from input file.
203 	int fileFormat = afGetFileFormat(inFile, NULL);
204 	int channelCount = afGetChannels(inFile, AF_DEFAULT_TRACK);
205 	double sampleRate = afGetRate(inFile, AF_DEFAULT_TRACK);
206 	int sampleFormat, sampleWidth;
207 	afGetSampleFormat(inFile, AF_DEFAULT_TRACK, &sampleFormat, &sampleWidth);
208 
209 	// Initialize output audio format parameters.
210 	AFfilesetup outFileSetup = afNewFileSetup();
211 
212 	if (outFileFormat == -1)
213 		outFileFormat = fileFormat;
214 
215 	if (outSampleFormat == -1 || outSampleWidth == -1)
216 	{
217 		outSampleFormat = sampleFormat;
218 		outSampleWidth = sampleWidth;
219 	}
220 
221 	if (outChannelCount == -1)
222 		outChannelCount = channelCount;
223 
224 	afInitFileFormat(outFileSetup, outFileFormat);
225 	afInitCompression(outFileSetup, AF_DEFAULT_TRACK, outCompression);
226 	afInitSampleFormat(outFileSetup, AF_DEFAULT_TRACK, outSampleFormat,
227 		outSampleWidth);
228 	afInitChannels(outFileSetup, AF_DEFAULT_TRACK, outChannelCount);
229 	afInitRate(outFileSetup, AF_DEFAULT_TRACK, sampleRate);
230 
231 	AFfilehandle outFile = afOpenFile(outFileName, "w", outFileSetup);
232 	if (!outFile)
233 	{
234 		printf("Could not open file '%s' for writing.\n", outFileName);
235 		return EXIT_FAILURE;
236 	}
237 
238 	afFreeFileSetup(outFileSetup);
239 
240 	/*
241 		Set the output file's virtual audio format parameters
242 		to match the audio format parameters of the input file.
243 	*/
244 	afSetVirtualChannels(outFile, AF_DEFAULT_TRACK, channelCount);
245 	afSetVirtualSampleFormat(outFile, AF_DEFAULT_TRACK, sampleFormat,
246 		sampleWidth);
247 
248 	bool success = copyaudiodata(inFile, outFile, AF_DEFAULT_TRACK);
249 
250 	afCloseFile(inFile);
251 	afCloseFile(outFile);
252 
253 	if (!success)
254 	{
255 		unlink(outFileName);
256 		return EXIT_FAILURE;
257 	}
258 
259 	printfileinfo(inFileName);
260 	putchar('\n');
261 	printfileinfo(outFileName);
262 
263 	return EXIT_SUCCESS;
264 }
265 
printusage(void)266 void printusage (void)
267 {
268 	printf("usage: sfconvert infile outfile [ options ... ] [ output keywords ... ]\n");
269 	printf("\n");
270 
271 	printf("Where keywords specify format of input or output soundfile:\n");
272 	printf("    format f       file format f (see below)\n");
273 	printf("    compression c  compression format c (see below)\n");
274 	printf("    byteorder e    endian (e is big or little)\n");
275 	printf("    channels n     n-channel file (1 or 2)\n");
276 	printf("    integer n s    n-bit integer file, where s is one of\n");
277 	printf("                       2scomp: 2's complement signed data\n");
278 	printf("                       unsigned: unsigned data\n");
279 	printf("    float m        floating point file, maxamp m (usually 1.0)\n");
280 	printf("\n");
281 
282 	printf("Currently supported file formats are:\n");
283 	printf("\n");
284 	printf("    aiff    Audio Interchange File Format\n");
285 	printf("    aifc    AIFF-C File Format\n");
286 	printf("    next    NeXT/Sun Format\n");
287 	printf("    wave    MS RIFF WAVE Format\n");
288 	printf("    bics    Berkeley/IRCAM/CARL Sound File Format\n");
289 	printf("    smp     Sample Vision Format\n");
290 	printf("    voc     Creative Voice File\n");
291 	printf("    nist    NIST SPHERE Format\n");
292 	printf("    caf     Core Audio Format\n");
293 	printf("\n");
294 
295 	printf("Currently supported compression formats are:\n");
296 	printf("\n");
297 	printf("    ulaw    G.711 u-law\n");
298 	printf("    alaw    G.711 A-law\n");
299 	printf("    ima     IMA ADPCM\n");
300 	printf("    msadpcm MS ADPCM\n");
301 	printf("    flac    FLAC\n");
302 	printf("    alac    Apple Lossless Audio Codec\n");
303 	printf("\n");
304 }
305 
usageerror(void)306 void usageerror (void)
307 {
308 	printusage();
309 	exit(EXIT_FAILURE);
310 }
311 
printversion(void)312 void printversion (void)
313 {
314 	printf("sfconvert: Audio File Library version %s\n", VERSION);
315 }
316 
317 /*
318 	Copy audio data from one file to another.  This function
319 	assumes that the virtual sample formats of the two files
320 	match.
321 */
copyaudiodata(AFfilehandle infile,AFfilehandle outfile,int trackid)322 bool copyaudiodata (AFfilehandle infile, AFfilehandle outfile, int trackid)
323 {
324 	int frameSize = afGetVirtualFrameSize(infile, trackid, 1);
325 
326 	const int kBufferFrameCount = 65536;
327 	void *buffer = malloc(kBufferFrameCount * frameSize);
328 
329 	AFframecount totalFrames = afGetFrameCount(infile, AF_DEFAULT_TRACK);
330 	AFframecount totalFramesWritten = 0;
331 
332 	bool success = true;
333 
334 	while (totalFramesWritten < totalFrames)
335 	{
336 		AFframecount framesToRead = totalFrames - totalFramesWritten;
337 		if (framesToRead > kBufferFrameCount)
338 			framesToRead = kBufferFrameCount;
339 
340 		AFframecount framesRead = afReadFrames(infile, trackid, buffer,
341 			framesToRead);
342 
343 		if (framesRead < framesToRead)
344 		{
345 			fprintf(stderr, "Bad read of audio track data.\n");
346 			success = false;
347 			break;
348 		}
349 
350 		AFframecount framesWritten = afWriteFrames(outfile, trackid, buffer,
351 			framesRead);
352 
353 		if (framesWritten < framesRead)
354 		{
355 			fprintf(stderr, "Bad write of audio track data.\n");
356 			success = false;
357 			break;
358 		}
359 
360 		totalFramesWritten += framesWritten;
361 	}
362 
363 	free(buffer);
364 
365 	return success;
366 }
367