this repo has no description
1/* Copyright: � Copyright 2005 Apple Computer, Inc. All rights reserved.
2
3 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
4 ("Apple") in consideration of your agreement to the following terms, and your
5 use, installation, modification or redistribution of this Apple software
6 constitutes acceptance of these terms. If you do not agree with these terms,
7 please do not use, install, modify or redistribute this Apple software.
8
9 In consideration of your agreement to abide by the following terms, and subject
10 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
11 copyrights in this original Apple software (the "Apple Software"), to use,
12 reproduce, modify and redistribute the Apple Software, with or without
13 modifications, in source and/or binary forms; provided that if you redistribute
14 the Apple Software in its entirety and without modifications, you must retain
15 this notice and the following text and disclaimers in all such redistributions of
16 the Apple Software. Neither the name, trademarks, service marks or logos of
17 Apple Computer, Inc. may be used to endorse or promote products derived from the
18 Apple Software without specific prior written permission from Apple. Except as
19 expressly stated in this notice, no other rights or licenses, express or implied,
20 are granted by Apple herein, including but not limited to any patent rights that
21 may be infringed by your derivative works or by other works in which the Apple
22 Software may be incorporated.
23
24 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
25 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
26 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
28 COMBINATION WITH YOUR PRODUCTS.
29
30 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
31 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
32 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
34 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
35 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
36 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37*/
38/*=============================================================================
39 Interleaver.cpp
40
41=============================================================================*/
42
43#include "CAAudioFile.h"
44#include "CAXException.h"
45#include <unistd.h>
46#include "CAChannelLayouts.h"
47#include "CAFilePathUtils.h"
48
49static void usage()
50{
51 fprintf(stderr,
52 "Usage:\n"
53 "%s [option...] input_file... -o output_file\n\n"
54 "Options: (may appear before or after arguments)\n"
55 " { -l | --channellayout } layout_tag\n"
56 " layout_tag: name of a constant from CoreAudioTypes.h\n"
57 " (prefix \"kAudioChannelLayoutTag_\" may be omitted)\n",
58 getprogname()
59 );
60 exit(1);
61}
62
63
64void Interleave(int nInputs, const char *infilenames[], const char *outfilename, const CAAudioChannelLayout *layout)
65{
66 const UInt32 kBufferSizeFrames = 0x8000;
67 const UInt32 kBufferSizeBytes = kBufferSizeFrames * sizeof(Float32);
68 class FileAndBuffer : public CAAudioFile {
69 public:
70 FileAndBuffer() : mBuf(NULL), mPtrs(NULL) { }
71 ~FileAndBuffer() { delete mBuf; delete mPtrs; }
72
73 CABufferList * mBuf;
74 CABufferList * mPtrs;
75 };
76 FileAndBuffer *infiles = new FileAndBuffer[nInputs], *file;
77 FileAndBuffer outfile;
78 int i;
79 UInt32 outputChannels = 0;
80 double sampleRate = 0.;
81 UInt32 maxBitDepth = 0;
82 CAStreamBasicDescription clientFormat;
83 bool outFileCreated = false;
84
85 try {
86 // set up input files
87 for (i = 0; i < nInputs; ++i) {
88 file = &infiles[i];
89 file->Open(infilenames[i]);
90 const CAStreamBasicDescription &fmt = file->GetFileDataFormat();
91 //fmt.PrintFormat(stdout, "", "input file");
92 XThrowIf(fmt.mFormatID != kAudioFormatLinearPCM, -1, "input files must be PCM");
93 outputChannels += fmt.mChannelsPerFrame;
94 if (sampleRate == 0.)
95 sampleRate = fmt.mSampleRate;
96 else
97 XThrowIf(fmt.mSampleRate != sampleRate, -1, "input files must have the same sample rate");
98 if (fmt.mBitsPerChannel > maxBitDepth)
99 maxBitDepth = fmt.mBitsPerChannel;
100 clientFormat.mSampleRate = sampleRate;
101 clientFormat.SetCanonical(fmt.mChannelsPerFrame, false); // deinterleaved
102 file->SetClientFormat(clientFormat, NULL);
103 file->mBuf = CABufferList::New("readbuf", clientFormat);
104 file->mBuf->AllocateBuffers(kBufferSizeBytes);
105 file->mPtrs = CABufferList::New("readptrs", clientFormat);
106 //clientFormat.PrintFormat(stdout, "", "input client");
107 }
108
109 if (layout != NULL) {
110 if (AudioChannelLayoutTag_GetNumberOfChannels(layout->Tag()) != outputChannels) {
111 fprintf(stderr, "Channel layout tag '%s' is inappropriate for %lu channels of audio -- aborting\n",
112 CAChannelLayouts::ConstantToString(layout->Tag()), outputChannels);
113 exit(2);
114 }
115 }
116
117 // prepare output file format
118 CAStreamBasicDescription outfmt;
119 outfmt.mSampleRate = sampleRate;
120 outfmt.mFormatID = kAudioFormatLinearPCM;
121 outfmt.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
122 outfmt.mBitsPerChannel = maxBitDepth;
123 outfmt.mChannelsPerFrame = outputChannels;
124 outfmt.mBytesPerPacket = outfmt.mBytesPerFrame = outputChannels * (maxBitDepth >> 3);
125 outfmt.mFramesPerPacket = 1;
126 //outfmt.PrintFormat(stdout, "", "output file");
127
128 unlink(outfilename);
129 FSRef parentDir;
130 CFStringRef outName;
131 XThrowIfError(PosixPathToParentFSRefAndName(outfilename, parentDir, outName), "Couldn't locate output directory");
132 outfile.CreateNew(parentDir, outName, kAudioFileAIFFType, outfmt, layout ? &layout->Layout() : NULL);
133 outFileCreated = true;
134
135 // create the output file and buffers
136 clientFormat.mSampleRate = sampleRate;
137 clientFormat.SetCanonical(outputChannels, false);
138 outfile.SetClientFormat(clientFormat, NULL);
139 //clientFormat.PrintFormat(stdout, "", "output client");
140
141 outfile.mPtrs = CABufferList::New("writeptrs", clientFormat);
142
143 AudioBufferList &writebufs = outfile.mPtrs->GetModifiableBufferList();
144 int outbuf = 0;
145 for (i = 0; i < nInputs; ++i) {
146 file = &infiles[i];
147 const CABufferList *bl = file->mBuf;
148 const AudioBufferList &readbufs = bl->GetBufferList();
149 memcpy(&writebufs.mBuffers[outbuf], &readbufs.mBuffers[0],
150 readbufs.mNumberBuffers * sizeof(AudioBuffer));
151 outbuf += readbufs.mNumberBuffers;
152 }
153
154 while (true) {
155 UInt32 maxFramesRead = 0;
156 UInt32 nframes;
157 for (i = 0; i < nInputs; ++i) {
158 file = &infiles[i];
159 file->mPtrs->SetFrom(file->mBuf);
160 nframes = kBufferSizeFrames;
161 AudioBufferList &readbufs = file->mPtrs->GetModifiableBufferList();
162 file->Read(nframes, &readbufs);
163 //CAShowAudioBufferList(&readbufs, 8, 0);
164 if (nframes > maxFramesRead)
165 maxFramesRead = nframes;
166 if (nframes < kBufferSizeFrames)
167 file->mPtrs->PadWithZeroes(kBufferSizeBytes);
168 }
169 if (maxFramesRead == 0)
170 break;
171
172 if (maxFramesRead < kBufferSizeFrames)
173 outfile.mPtrs->SetNumBytes(maxFramesRead * sizeof(Float32));
174 //CAShowAudioBufferList(&writebufs, 8, 0);
175 outfile.Write(maxFramesRead, &writebufs);
176 if (maxFramesRead < kBufferSizeFrames)
177 break;
178 }
179 }
180 catch (...) {
181 if (outFileCreated)
182 unlink(outfilename);
183 delete[] infiles;
184 throw;
185 }
186 outfile.Close();
187 // input files are closed from destructors
188 delete[] infiles;
189}
190
191static void MissingArgument()
192{
193 fprintf(stderr, "missing argument\n\n");
194 usage();
195}
196
197int main(int argc, const char *argv[])
198{
199 int nins = 0;
200 static const int MAX_INPUT_FILES = 32;
201 const char *infiles[MAX_INPUT_FILES];
202 const char *outfile = NULL;
203 UInt32 layoutTag = 0;
204
205 for (int i = 1; i < argc; ++i) {
206 const char *arg = argv[i];
207 if (arg[0] != '-') {
208 if (nins >= MAX_INPUT_FILES) {
209 fprintf(stderr, "too many input files\n\n");
210 usage();
211 }
212 infiles[nins++] = arg;
213 } else {
214 arg += 1;
215 if (arg[0] == 'o') {
216 if (++i == argc) MissingArgument();
217 outfile = argv[i];
218 } else if (arg[0] == 'l' || !strcmp(arg, "-channellayout")) {
219 if (++i == argc) MissingArgument();
220 layoutTag = CAChannelLayouts::StringToConstant(argv[i]);
221 if (layoutTag == CAChannelLayouts::kInvalidTag) {
222 fprintf(stderr, "unknown channel layout tag: %s\n\n", argv[i]);
223 usage();
224 }
225 } else {
226 fprintf(stderr, "unknown argument: %s\n\n", arg - 1);
227 usage();
228 }
229 }
230 }
231
232 if (nins < 2 || outfile == NULL)
233 usage();
234
235 try {
236 if (layoutTag != 0) {
237 CAAudioChannelLayout layout = CAAudioChannelLayout(layoutTag);
238 Interleave(nins, infiles, outfile, &layout);
239 } else
240 Interleave(nins, infiles, outfile, NULL);
241 }
242 catch (CAXException &e) {
243 char buf[256];
244 fprintf(stderr, "Error: %s (%s)\n", e.mOperation, CAXException::FormatError(buf, e.mError));
245 return 1;
246 }
247 catch (...) {
248 fprintf(stderr, "An unknown error occurred\n");
249 return 1;
250 }
251 return 0;
252}