1 /*
2 Audio File Library
3 Copyright (C) 2011-2013, Michael Pruett <michael@68k.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301 USA
19 */
20
21 #include "config.h"
22 #include "CAF.h"
23
24 #include "Buffer.h"
25 #include "File.h"
26 #include "PacketTable.h"
27 #include "Setup.h"
28 #include "Tag.h"
29 #include "Track.h"
30 #include "byteorder.h"
31 #include "util.h"
32
33 #include <stdint.h>
34 #include <string.h>
35 #include <string>
36 #include <vector>
37
38 const int _af_caf_compression_types[_AF_CAF_NUM_COMPTYPES] =
39 {
40 AF_COMPRESSION_G711_ULAW,
41 AF_COMPRESSION_G711_ALAW,
42 AF_COMPRESSION_IMA,
43 AF_COMPRESSION_ALAC
44 };
45
46 enum
47 {
48 kCAFLinearPCMFormatFlagIsFloat = (1L << 0),
49 kCAFLinearPCMFormatFlagIsLittleEndian = (1L << 1)
50 };
51
52 enum
53 {
54 kALACFormatFlag_16BitSourceData = 1,
55 kALACFormatFlag_20BitSourceData = 2,
56 kALACFormatFlag_24BitSourceData = 3,
57 kALACFormatFlag_32BitSourceData = 4
58 };
59
60 static const unsigned kALACDefaultFramesPerPacket = 4096;
61
62 static const _AFfilesetup cafDefaultFileSetup =
63 {
64 _AF_VALID_FILESETUP, // valid
65 AF_FILE_CAF, // fileFormat
66 true, // trackSet
67 true, // instrumentSet
68 true, // miscellaneousSet
69 1, // trackCount
70 NULL, // tracks
71 1, // instrumentCount
72 NULL, // instruments
73 0, // miscellaneousCount
74 NULL // miscellaneous
75 };
76
CAFFile()77 CAFFile::CAFFile() :
78 m_dataOffset(-1),
79 m_cookieDataOffset(-1)
80 {
81 setFormatByteOrder(AF_BYTEORDER_BIGENDIAN);
82 }
83
~CAFFile()84 CAFFile::~CAFFile()
85 {
86 }
87
recognize(File * file)88 bool CAFFile::recognize(File *file)
89 {
90 file->seek(0, File::SeekFromBeginning);
91 uint8_t buffer[8];
92 if (file->read(buffer, 8) != 8 || memcmp(buffer, "caff", 4) != 0)
93 return false;
94 const uint8_t versionAndFlags[4] = { 0, 1, 0, 0 };
95 if (memcmp(buffer + 4, versionAndFlags, 4) != 0)
96 return false;
97 return true;
98 }
99
readInit(AFfilesetup setup)100 status CAFFile::readInit(AFfilesetup setup)
101 {
102 m_fh->seek(8, File::SeekFromBeginning);
103
104 if (!allocateTrack())
105 return AF_FAIL;
106
107 off_t currentOffset = m_fh->tell();
108 off_t fileLength = m_fh->length();
109
110 while (currentOffset < fileLength)
111 {
112 Tag chunkType;
113 int64_t chunkLength;
114 if (!readTag(&chunkType) ||
115 !readS64(&chunkLength))
116 return AF_FAIL;
117
118 currentOffset += 12;
119
120 if (chunkType == "data" && chunkLength == -1)
121 chunkLength = fileLength - currentOffset;
122 else if (chunkLength < 0)
123 _af_error(AF_BAD_HEADER,
124 "invalid chunk length %jd for chunk type %s\n",
125 static_cast<intmax_t>(chunkLength), chunkType.name().c_str());
126
127 if (chunkType == "desc")
128 {
129 if (parseDescription(chunkType, chunkLength) == AF_FAIL)
130 return AF_FAIL;
131 }
132 else if (chunkType == "data")
133 {
134 if (parseData(chunkType, chunkLength) == AF_FAIL)
135 return AF_FAIL;
136 }
137 else if (chunkType == "pakt")
138 {
139 if (parsePacketTable(chunkType, chunkLength) == AF_FAIL)
140 return AF_FAIL;
141 }
142 else if (chunkType == "kuki")
143 {
144 if (parseCookieData(chunkType, chunkLength) == AF_FAIL)
145 return AF_FAIL;
146 }
147
148 currentOffset = m_fh->seek(currentOffset + chunkLength,
149 File::SeekFromBeginning);
150 }
151
152 return AF_SUCCEED;
153 }
154
writeInit(AFfilesetup setup)155 status CAFFile::writeInit(AFfilesetup setup)
156 {
157 if (initFromSetup(setup) == AF_FAIL)
158 return AF_FAIL;
159
160 initCompressionParams();
161
162 Tag caff("caff");
163 if (!writeTag(&caff)) return AF_FAIL;
164 const uint8_t versionAndFlags[4] = { 0, 1, 0, 0 };
165 if (m_fh->write(versionAndFlags, 4) != 4) return AF_FAIL;
166
167 if (writeDescription() == AF_FAIL)
168 return AF_FAIL;
169 if (writeCookieData() == AF_FAIL)
170 return AF_FAIL;
171 if (writeData(false) == AF_FAIL)
172 return AF_FAIL;
173
174 return AF_SUCCEED;
175 }
176
completeSetup(AFfilesetup setup)177 AFfilesetup CAFFile::completeSetup(AFfilesetup setup)
178 {
179 if (setup->trackSet && setup->trackCount != 1)
180 {
181 _af_error(AF_BAD_NUMTRACKS, "CAF file must have 1 track");
182 return AF_NULL_FILESETUP;
183 }
184
185 TrackSetup *track = &setup->tracks[0];
186
187 if (track->sampleFormatSet)
188 {
189 if (track->f.isUnsigned())
190 {
191 _af_error(AF_BAD_FILEFMT, "CAF format does not support unsigned data");
192 return AF_NULL_FILESETUP;
193 }
194 }
195 else
196 _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP,
197 track->f.sampleWidth);
198
199 if (track->f.isSigned() && (track->f.sampleWidth < 1 || track->f.sampleWidth > 32))
200 {
201 _af_error(AF_BAD_WIDTH,
202 "invalid sample width %d for CAF file (must be 1-32)",
203 track->f.sampleWidth);
204 return AF_NULL_FILESETUP;
205 }
206
207 if (!track->byteOrderSet)
208 track->f.byteOrder = _AF_BYTEORDER_NATIVE;
209
210 if (track->f.compressionType != AF_COMPRESSION_NONE &&
211 track->f.compressionType != AF_COMPRESSION_G711_ULAW &&
212 track->f.compressionType != AF_COMPRESSION_G711_ALAW &&
213 track->f.compressionType != AF_COMPRESSION_IMA &&
214 track->f.compressionType != AF_COMPRESSION_ALAC)
215 {
216 _af_error(AF_BAD_COMPTYPE,
217 "compression format %d not supported in CAF file",
218 track->f.compressionType);
219 return AF_NULL_FILESETUP;
220 }
221
222 if (track->markersSet && track->markerCount)
223 {
224 _af_error(AF_BAD_NOT_IMPLEMENTED, "CAF does not yet support markers");
225 return AF_NULL_FILESETUP;
226 }
227
228 if (track->aesDataSet)
229 {
230 _af_error(AF_BAD_FILESETUP, "CAF does not support AES data");
231 return AF_NULL_FILESETUP;
232 }
233
234 return _af_filesetup_copy(setup, &cafDefaultFileSetup, true);
235 }
236
update()237 status CAFFile::update()
238 {
239 if (writeCookieData() == AF_FAIL)
240 return AF_FAIL;
241 if (writeData(true) == AF_FAIL)
242 return AF_FAIL;
243 if (writePacketTable() == AF_FAIL)
244 return AF_FAIL;
245 return AF_SUCCEED;
246 }
247
parseDescription(const Tag &,int64_t)248 status CAFFile::parseDescription(const Tag &, int64_t)
249 {
250 double sampleRate;
251 Tag formatID;
252 uint32_t formatFlags;
253 uint32_t bytesPerPacket;
254 uint32_t framesPerPacket;
255 uint32_t channelsPerFrame;
256 uint32_t bitsPerChannel;
257 if (!readDouble(&sampleRate) ||
258 !readTag(&formatID) ||
259 !readU32(&formatFlags) ||
260 !readU32(&bytesPerPacket) ||
261 !readU32(&framesPerPacket) ||
262 !readU32(&channelsPerFrame) ||
263 !readU32(&bitsPerChannel))
264 return AF_FAIL;
265
266 if (!channelsPerFrame)
267 {
268 _af_error(AF_BAD_CHANNELS, "invalid file with 0 channels");
269 return AF_FAIL;
270 }
271
272 Track *track = getTrack();
273 track->f.channelCount = channelsPerFrame;
274 track->f.sampleWidth = bitsPerChannel;
275 track->f.sampleRate = sampleRate;
276 track->f.framesPerPacket = 1;
277
278 if (formatID == "lpcm")
279 {
280 track->f.compressionType = AF_COMPRESSION_NONE;
281 if (formatFlags & kCAFLinearPCMFormatFlagIsFloat)
282 {
283 if (bitsPerChannel != 32 && bitsPerChannel != 64)
284 {
285 _af_error(AF_BAD_WIDTH, "invalid bits per sample %d for floating-point audio data", bitsPerChannel);
286 return AF_FAIL;
287 }
288 track->f.sampleFormat = bitsPerChannel == 32 ? AF_SAMPFMT_FLOAT :
289 AF_SAMPFMT_DOUBLE;
290 }
291 else
292 {
293 track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
294 }
295 track->f.byteOrder = (formatFlags & kCAFLinearPCMFormatFlagIsLittleEndian) ?
296 AF_BYTEORDER_LITTLEENDIAN : AF_BYTEORDER_BIGENDIAN;
297
298 if (_af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth) == AF_FAIL)
299 return AF_FAIL;
300
301 track->f.computeBytesPerPacketPCM();
302 return AF_SUCCEED;
303 }
304 else if (formatID == "ulaw")
305 {
306 track->f.compressionType = AF_COMPRESSION_G711_ULAW;
307 track->f.byteOrder = _AF_BYTEORDER_NATIVE;
308 _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, 16);
309 track->f.bytesPerPacket = channelsPerFrame;
310 return AF_SUCCEED;
311 }
312 else if (formatID == "alaw")
313 {
314 track->f.compressionType = AF_COMPRESSION_G711_ALAW;
315 track->f.byteOrder = _AF_BYTEORDER_NATIVE;
316 _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, 16);
317 track->f.bytesPerPacket = channelsPerFrame;
318 return AF_SUCCEED;
319 }
320 else if (formatID == "ima4")
321 {
322 track->f.compressionType = AF_COMPRESSION_IMA;
323 track->f.byteOrder = _AF_BYTEORDER_NATIVE;
324 _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, 16);
325 initIMACompressionParams();
326 return AF_SUCCEED;
327 }
328 else if (formatID == "alac")
329 {
330 track->f.compressionType = AF_COMPRESSION_ALAC;
331 track->f.byteOrder = _AF_BYTEORDER_NATIVE;
332 switch (formatFlags)
333 {
334 case kALACFormatFlag_16BitSourceData:
335 track->f.sampleWidth = 16; break;
336 case kALACFormatFlag_20BitSourceData:
337 track->f.sampleWidth = 20; break;
338 case kALACFormatFlag_24BitSourceData:
339 track->f.sampleWidth = 24; break;
340 case kALACFormatFlag_32BitSourceData:
341 track->f.sampleWidth = 32; break;
342 default:
343 _af_error(AF_BAD_CODEC_TYPE,
344 "unsupported format flags for ALAC: %u", formatFlags);
345 return AF_FAIL;
346 }
347 _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP,
348 track->f.sampleWidth);
349 track->f.framesPerPacket = framesPerPacket;
350 track->f.bytesPerPacket = 0;
351 return AF_SUCCEED;
352 }
353 else
354 {
355 _af_error(AF_BAD_NOT_IMPLEMENTED, "Compression type %s not supported",
356 formatID.name().c_str());
357 return AF_FAIL;
358 }
359 }
360
parseData(const Tag & tag,int64_t length)361 status CAFFile::parseData(const Tag &tag, int64_t length)
362 {
363 uint32_t editCount;
364 if (!readU32(&editCount))
365 return AF_FAIL;
366
367 Track *track = getTrack();
368 if (length == -1)
369 track->data_size = m_fh->length() - m_fh->tell();
370 else
371 track->data_size = length - 4;
372 track->fpos_first_frame = m_fh->tell();
373
374 track->computeTotalFileFrames();
375 return AF_SUCCEED;
376 }
377
readBERInteger(const uint8_t * input,size_t * numBytes)378 static uint32_t readBERInteger(const uint8_t *input, size_t *numBytes)
379 {
380 uint32_t result = 0;
381 uint8_t data;
382 size_t size = 0;
383 do
384 {
385 data = input[size];
386 result = (result << 7) | (data & 0x7f);
387 if (++size > 5)
388 return 0;
389 } while ((data & 0x80) && size < *numBytes);
390 *numBytes = size;
391 return result;
392 }
393
encodeBERInteger(uint32_t value,uint8_t * buffer,size_t * numBytes)394 static void encodeBERInteger(uint32_t value, uint8_t *buffer, size_t *numBytes)
395 {
396 if ((value & 0x7f) == value)
397 {
398 *numBytes = 1;
399 buffer[0] = value;
400 }
401 else if ((value & 0x3fff) == value)
402 {
403 *numBytes = 2;
404 buffer[0] = (value >> 7) | 0x80;
405 buffer[1] = value & 0x7f;
406 }
407 else if ((value & 0x1fffff) == value)
408 {
409 *numBytes = 3;
410 buffer[0] = (value >> 14) | 0x80;
411 buffer[1] = ((value >> 7) & 0x7f) | 0x80;
412 buffer[2] = value & 0x7f;
413 }
414 else if ((value & 0x0fffffff) == value)
415 {
416 *numBytes = 4;
417 buffer[0] = (value >> 21) | 0x80;
418 buffer[1] = ((value >> 14) & 0x7f) | 0x80;
419 buffer[2] = ((value >> 7) & 0x7f) | 0x80;
420 buffer[3] = value & 0x7f;
421 }
422 else
423 {
424 *numBytes = 5;
425 buffer[0] = (value >> 28) | 0x80;
426 buffer[1] = ((value >> 21) & 0x7f) | 0x80;
427 buffer[2] = ((value >> 14) & 0x7f) | 0x80;
428 buffer[3] = ((value >> 7) & 0x7f) | 0x80;
429 buffer[4] = value & 0x7f;
430 }
431 }
432
parsePacketTable(const Tag & tag,int64_t length)433 status CAFFile::parsePacketTable(const Tag &tag, int64_t length)
434 {
435 if (length < 24)
436 return AF_FAIL;
437
438 int64_t numPackets;
439 int64_t numValidFrames;
440 int32_t primingFrames;
441 int32_t remainderFrames;
442 if (!readS64(&numPackets) ||
443 !readS64(&numValidFrames) ||
444 !readS32(&primingFrames) ||
445 !readS32(&remainderFrames))
446 {
447 return AF_FAIL;
448 }
449
450 if (!numPackets)
451 return AF_SUCCEED;
452
453 int64_t tableLength = length - 24;
454
455 SharedPtr<Buffer> buffer = new Buffer(tableLength);
456 if (m_fh->read(buffer->data(), tableLength) != tableLength)
457 return AF_FAIL;
458
459 SharedPtr<PacketTable> packetTable = new PacketTable(numValidFrames,
460 primingFrames, remainderFrames);
461
462 const uint8_t *data = static_cast<const uint8_t *>(buffer->data());
463 size_t position = 0;
464 while (position < buffer->size())
465 {
466 size_t sizeRemaining = buffer->size() - position;
467 uint32_t bytesPerPacket = readBERInteger(data + position, &sizeRemaining);
468 if (bytesPerPacket == 0)
469 break;
470 packetTable->append(bytesPerPacket);
471 position += sizeRemaining;
472 }
473
474 assert(numPackets == packetTable->numPackets());
475
476 Track *track = getTrack();
477 track->m_packetTable = packetTable;
478 track->totalfframes = numValidFrames;
479
480 return AF_SUCCEED;
481 }
482
parseCookieData(const Tag & tag,int64_t length)483 status CAFFile::parseCookieData(const Tag &tag, int64_t length)
484 {
485 m_codecData = new Buffer(length);
486 if (m_fh->read(m_codecData->data(), length) != length)
487 return AF_FAIL;
488
489 AUpvlist pv = AUpvnew(2);
490
491 AUpvsetparam(pv, 0, _AF_CODEC_DATA_SIZE);
492 AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
493 long l = length;
494 AUpvsetval(pv, 0, &l);
495
496 AUpvsetparam(pv, 1, _AF_CODEC_DATA);
497 AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR);
498 void *v = m_codecData->data();
499 AUpvsetval(pv, 1, &v);
500
501 Track *track = getTrack();
502 track->f.compressionParams = pv;
503
504 return AF_SUCCEED;
505 }
506
writeDescription()507 status CAFFile::writeDescription()
508 {
509 Track *track = getTrack();
510
511 Tag desc("desc");
512 int64_t chunkLength = 32;
513 double sampleRate = track->f.sampleRate;
514 Tag formatID("lpcm");
515 uint32_t formatFlags = 0;
516 if (track->f.byteOrder == AF_BYTEORDER_LITTLEENDIAN)
517 formatFlags |= kCAFLinearPCMFormatFlagIsLittleEndian;
518 if (track->f.isFloat())
519 formatFlags |= kCAFLinearPCMFormatFlagIsFloat;
520 uint32_t bytesPerPacket = track->f.bytesPerFrame(false);
521 uint32_t framesPerPacket = 1;
522 uint32_t channelsPerFrame = track->f.channelCount;
523 uint32_t bitsPerChannel = track->f.sampleWidth;
524
525 if (track->f.compressionType == AF_COMPRESSION_G711_ULAW)
526 {
527 formatID = "ulaw";
528 formatFlags = 0;
529 bytesPerPacket = channelsPerFrame;
530 bitsPerChannel = 8;
531 }
532 else if (track->f.compressionType == AF_COMPRESSION_G711_ALAW)
533 {
534 formatID = "alaw";
535 formatFlags = 0;
536 bytesPerPacket = channelsPerFrame;
537 bitsPerChannel = 8;
538 }
539 else if (track->f.compressionType == AF_COMPRESSION_IMA)
540 {
541 formatID = "ima4";
542 formatFlags = 0;
543 bytesPerPacket = track->f.bytesPerPacket;
544 framesPerPacket = track->f.framesPerPacket;
545 bitsPerChannel = 16;
546 }
547 else if (track->f.compressionType == AF_COMPRESSION_ALAC)
548 {
549 formatID = "alac";
550 switch (track->f.sampleWidth)
551 {
552 case 16: formatFlags = kALACFormatFlag_16BitSourceData; break;
553 case 20: formatFlags = kALACFormatFlag_20BitSourceData; break;
554 case 24: formatFlags = kALACFormatFlag_24BitSourceData; break;
555 case 32: formatFlags = kALACFormatFlag_32BitSourceData; break;
556 }
557 bytesPerPacket = track->f.bytesPerPacket;
558 framesPerPacket = track->f.framesPerPacket;
559 }
560
561 if (!writeTag(&desc) ||
562 !writeS64(&chunkLength) ||
563 !writeDouble(&sampleRate) ||
564 !writeTag(&formatID) ||
565 !writeU32(&formatFlags) ||
566 !writeU32(&bytesPerPacket) ||
567 !writeU32(&framesPerPacket) ||
568 !writeU32(&channelsPerFrame) ||
569 !writeU32(&bitsPerChannel))
570 return AF_FAIL;
571 return AF_SUCCEED;
572 }
573
writeData(bool update)574 status CAFFile::writeData(bool update)
575 {
576 Track *track = getTrack();
577
578 if (m_dataOffset == -1)
579 m_dataOffset = m_fh->tell();
580 else
581 m_fh->seek(m_dataOffset, File::SeekFromBeginning);
582
583 Tag data("data");
584 int64_t dataLength = -1;
585 uint32_t editCount = 0;
586 if (update)
587 dataLength = track->data_size + 4;
588
589 if (!writeTag(&data) ||
590 !writeS64(&dataLength) ||
591 !writeU32(&editCount))
592 return AF_FAIL;
593 if (track->fpos_first_frame == 0)
594 track->fpos_first_frame = m_fh->tell();
595 return AF_SUCCEED;
596 }
597
writePacketTable()598 status CAFFile::writePacketTable()
599 {
600 Track *track = getTrack();
601
602 m_fh->seek(track->fpos_after_data, File::SeekFromBeginning);
603
604 SharedPtr<PacketTable> packetTable = track->m_packetTable;
605 if (!packetTable)
606 return AF_SUCCEED;
607
608 int64_t numPackets = packetTable->numPackets();
609 int64_t numValidFrames = packetTable->numValidFrames();
610 int32_t primingFrames = packetTable->primingFrames();
611 int32_t remainderFrames = packetTable->remainderFrames();
612
613 SharedPtr<Buffer> buffer = new Buffer(packetTable->numPackets() * 5);
614
615 uint8_t *data = static_cast<uint8_t *>(buffer->data());
616 size_t position = 0;
617 for (unsigned i=0; i<packetTable->numPackets(); i++)
618 {
619 uint32_t bytesPerPacket = packetTable->bytesPerPacket(i);
620 size_t numBytes = 0;
621 encodeBERInteger(bytesPerPacket, data + position, &numBytes);
622 position += numBytes;
623 }
624
625 Tag pakt("pakt");
626 int64_t packetTableLength = 24 + position;
627
628 if (!writeTag(&pakt) ||
629 !writeS64(&packetTableLength) ||
630 !writeS64(&numPackets) ||
631 !writeS64(&numValidFrames) ||
632 !writeS32(&primingFrames) ||
633 !writeS32(&remainderFrames) ||
634 m_fh->write(buffer->data(), position) != static_cast<ssize_t>(position))
635 {
636 return AF_FAIL;
637 }
638
639 return AF_SUCCEED;
640 }
641
writeCookieData()642 status CAFFile::writeCookieData()
643 {
644 if (!m_codecData)
645 return AF_SUCCEED;
646
647 if (m_cookieDataOffset == -1)
648 m_cookieDataOffset = m_fh->tell();
649 else
650 m_fh->seek(m_cookieDataOffset, File::SeekFromBeginning);
651
652 Tag kuki("kuki");
653 int64_t cookieDataLength = m_codecData->size();
654 if (!writeTag(&kuki) ||
655 !writeS64(&cookieDataLength) ||
656 m_fh->write(m_codecData->data(), m_codecData->size()) != static_cast<ssize_t>(m_codecData->size()))
657 {
658 return AF_FAIL;
659 }
660
661 return AF_SUCCEED;
662 }
663
initCompressionParams()664 void CAFFile::initCompressionParams()
665 {
666 Track *track = getTrack();
667 if (track->f.compressionType == AF_COMPRESSION_IMA)
668 initIMACompressionParams();
669 else if (track->f.compressionType == AF_COMPRESSION_ALAC)
670 initALACCompressionParams();
671 }
672
initIMACompressionParams()673 void CAFFile::initIMACompressionParams()
674 {
675 Track *track = getTrack();
676
677 track->f.bytesPerPacket = 34 * track->f.channelCount;
678 track->f.framesPerPacket = 64;
679
680 AUpvlist pv = AUpvnew(1);
681 AUpvsetparam(pv, 0, _AF_IMA_ADPCM_TYPE);
682 AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
683 long l = _AF_IMA_ADPCM_TYPE_QT;
684 AUpvsetval(pv, 0, &l);
685
686 track->f.compressionParams = pv;
687 }
688
initALACCompressionParams()689 void CAFFile::initALACCompressionParams()
690 {
691 if (m_access == _AF_READ_ACCESS)
692 return;
693
694 Track *track = getTrack();
695
696 track->f.bytesPerPacket = 0;
697 track->f.framesPerPacket = kALACDefaultFramesPerPacket;
698
699 const unsigned kALACSpecificConfigSize = 24;
700 const unsigned kChannelAtomSize = 12;
701 const unsigned kALACAudioChannelLayoutSize = 12;
702
703 unsigned codecDataSize = kALACSpecificConfigSize;
704 if (track->f.channelCount > 2)
705 codecDataSize += kChannelAtomSize + kALACAudioChannelLayoutSize;
706 m_codecData = new Buffer(codecDataSize);
707 bzero(m_codecData->data(), m_codecData->size());
708
709 AUpvlist pv = AUpvnew(2);
710
711 AUpvsetparam(pv, 0, _AF_CODEC_DATA_SIZE);
712 AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
713 long l = codecDataSize;
714 AUpvsetval(pv, 0, &l);
715
716 AUpvsetparam(pv, 1, _AF_CODEC_DATA);
717 AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR);
718 void *v = m_codecData->data();
719 AUpvsetval(pv, 1, &v);
720
721 track->f.compressionParams = pv;
722
723 track->m_packetTable = new PacketTable();
724 }
725