3 Copyright (C) 1998-2000, Michael Pruett <michael@68k.org>
4 Copyright (C) 2000-2001, Silicon Graphics, Inc.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with this library; if not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307 USA.
25 This file contains routines for writing AIFF and AIFF-C format
39 #include "afinternal.h"
40 #include "audiofile.h"
42 #include "byteorder.h"
46 status
_af_aiff_update (AFfilehandle file
);
48 static status
WriteCOMM (AFfilehandle file
);
49 static status
WriteSSND (AFfilehandle file
);
50 static status
WriteMARK (AFfilehandle file
);
51 static status
WriteINST (AFfilehandle file
);
52 static status
WriteFVER (AFfilehandle file
);
53 static status
WriteAESD (AFfilehandle file
);
54 static status
WriteMiscellaneous (AFfilehandle file
);
56 static _AIFFInfo
*aiffinfo_new (void)
58 _AIFFInfo
*aiff
= _af_malloc(sizeof (_AIFFInfo
));
60 aiff
->miscellaneousPosition
= 0;
61 aiff
->FVER_offset
= 0;
62 aiff
->COMM_offset
= 0;
63 aiff
->MARK_offset
= 0;
64 aiff
->INST_offset
= 0;
65 aiff
->AESD_offset
= 0;
66 aiff
->SSND_offset
= 0;
71 status
_af_aiff_write_init (AFfilesetup setup
, AFfilehandle file
)
73 uint32_t fileSize
= 0;
76 assert(file
->fileFormat
== AF_FILE_AIFF
||
77 file
->fileFormat
== AF_FILE_AIFFC
);
79 if (_af_filesetup_make_handle(setup
, file
) == AF_FAIL
)
82 file
->formatSpecific
= aiffinfo_new();
84 af_fwrite("FORM", 4, 1, file
->fh
);
85 af_write_uint32_be(&fileSize
, file
->fh
);
87 if (file
->fileFormat
== AF_FILE_AIFF
)
88 af_fwrite("AIFF", 4, 1, file
->fh
);
89 else if (file
->fileFormat
== AF_FILE_AIFFC
)
90 af_fwrite("AIFC", 4, 1, file
->fh
);
92 if (file
->fileFormat
== AF_FILE_AIFFC
)
99 WriteMiscellaneous(file
);
105 status
_af_aiff_update (AFfilehandle file
)
112 track
= _af_filehandle_get_track(file
, AF_DEFAULT_TRACK
);
115 printf("_af_aiff_update called.\n");
118 /* Get the length of the file. */
119 length
= af_flength(file
->fh
);
122 /* Set the length of the FORM chunk. */
123 af_fseek(file
->fh
, 4, SEEK_SET
);
124 af_write_uint32_be(&length
, file
->fh
);
126 if (file
->fileFormat
== AF_FILE_AIFFC
)
133 WriteMiscellaneous(file
);
139 static status
WriteCOMM (const AFfilehandle file
)
148 unsigned char eb
[10];
150 uint8_t compressionTag
[4];
151 /* Pascal strings can occupy only 255 bytes (+ a size byte). */
152 char compressionName
[256];
154 isAIFFC
= file
->fileFormat
== AF_FILE_AIFFC
;
156 aiff
= file
->formatSpecific
;
159 If COMM_offset hasn't been set yet, set it to the
162 if (aiff
->COMM_offset
== 0)
163 aiff
->COMM_offset
= af_ftell(file
->fh
);
165 af_fseek(file
->fh
, aiff
->COMM_offset
, SEEK_SET
);
167 track
= _af_filehandle_get_track(file
, AF_DEFAULT_TRACK
);
171 if (track
->f
.compressionType
== AF_COMPRESSION_NONE
)
173 if (track
->f
.sampleFormat
== AF_SAMPFMT_TWOSCOMP
)
175 memcpy(compressionTag
, "NONE", 4);
176 strcpy(compressionName
, "not compressed");
178 else if (track
->f
.sampleFormat
== AF_SAMPFMT_FLOAT
)
180 memcpy(compressionTag
, "fl32", 4);
181 strcpy(compressionName
, "32-bit Floating Point");
183 else if (track
->f
.sampleFormat
== AF_SAMPFMT_DOUBLE
)
185 memcpy(compressionTag
, "fl64", 4);
186 strcpy(compressionName
, "64-bit Floating Point");
189 We disallow unsigned sample data for
190 AIFF files in _af_aiff_complete_setup,
191 so the next condition should never be
194 else if (track
->f
.sampleFormat
== AF_SAMPFMT_UNSIGNED
)
196 _af_error(AF_BAD_SAMPFMT
,
197 "AIFF/AIFF-C format does not support unsigned data");
202 else if (track
->f
.compressionType
== AF_COMPRESSION_G711_ULAW
)
204 memcpy(compressionTag
, "ulaw", 4);
205 strcpy(compressionName
, "CCITT G.711 u-law");
207 else if (track
->f
.compressionType
== AF_COMPRESSION_G711_ALAW
)
209 memcpy(compressionTag
, "alaw", 4);
210 strcpy(compressionName
, "CCITT G.711 A-law");
214 af_fwrite("COMM", 4, 1, file
->fh
);
217 For AIFF-C files, the length of the COMM chunk is 22
218 plus the length of the compression name plus the size
219 byte. If the length of the data is an odd number of
220 bytes, add a zero pad byte at the end, but don't
221 include the pad byte in the chunk's size.
224 chunkSize
= 22 + strlen(compressionName
) + 1;
227 af_write_uint32_be(&chunkSize
, file
->fh
);
229 /* number of channels, 2 bytes */
230 sb
= track
->f
.channelCount
;
231 af_write_uint16_be(&sb
, file
->fh
);
233 /* number of sample frames, 4 bytes */
234 lb
= track
->totalfframes
;
235 af_write_uint32_be(&lb
, file
->fh
);
237 /* sample size, 2 bytes */
238 sb
= track
->f
.sampleWidth
;
239 af_write_uint16_be(&sb
, file
->fh
);
241 /* sample rate, 10 bytes */
242 _af_convert_to_ieee_extended(track
->f
.sampleRate
, eb
);
243 af_fwrite(eb
, 10, 1, file
->fh
);
245 if (file
->fileFormat
== AF_FILE_AIFFC
)
247 af_fwrite(compressionTag
, 4, 1, file
->fh
);
249 af_write_pstring(compressionName
, file
->fh
);
256 The AESD chunk contains information pertinent to audio recording
259 static status
WriteAESD (const AFfilehandle file
)
267 aiff
= file
->formatSpecific
;
269 track
= _af_filehandle_get_track(file
, AF_DEFAULT_TRACK
);
271 if (!track
->hasAESData
)
274 if (aiff
->AESD_offset
== 0)
275 aiff
->AESD_offset
= af_ftell(file
->fh
);
277 af_fseek(file
->fh
, aiff
->AESD_offset
, SEEK_SET
);
279 if (af_fwrite("AESD", 4, 1, file
->fh
) < 1)
282 if (af_write_uint32_be(&size
, file
->fh
) == AF_FAIL
)
285 if (af_fwrite(track
->aesData
, 24, 1, file
->fh
) < 1)
291 static status
WriteSSND (AFfilehandle file
)
294 uint32_t chunkSize
, zero
= 0;
300 aiff
= file
->formatSpecific
;
302 track
= _af_filehandle_get_track(file
, AF_DEFAULT_TRACK
);
304 if (aiff
->SSND_offset
== 0)
305 aiff
->SSND_offset
= af_ftell(file
->fh
);
307 af_fseek(file
->fh
, aiff
->SSND_offset
, SEEK_SET
);
309 chunkSize
= (int) _af_format_frame_size(&track
->f
, false) *
310 track
->totalfframes
+ 8;
312 af_fwrite("SSND", 4, 1, file
->fh
);
313 af_write_uint32_be(&chunkSize
, file
->fh
);
316 af_write_uint32_be(&zero
, file
->fh
);
318 af_write_uint32_be(&zero
, file
->fh
);
320 if (track
->fpos_first_frame
== 0)
321 track
->fpos_first_frame
= af_ftell(file
->fh
);
326 static status
WriteINST (AFfilehandle file
)
329 struct _INST instrumentdata
;
333 instrumentdata
.sustainLoopPlayMode
=
334 afGetLoopMode(file
, AF_DEFAULT_INST
, 1);
335 instrumentdata
.sustainLoopBegin
=
336 afGetLoopStart(file
, AF_DEFAULT_INST
, 1);
337 instrumentdata
.sustainLoopEnd
=
338 afGetLoopEnd(file
, AF_DEFAULT_INST
, 1);
340 instrumentdata
.releaseLoopPlayMode
=
341 afGetLoopMode(file
, AF_DEFAULT_INST
, 2);
342 instrumentdata
.releaseLoopBegin
=
343 afGetLoopStart(file
, AF_DEFAULT_INST
, 2);
344 instrumentdata
.releaseLoopEnd
=
345 afGetLoopEnd(file
, AF_DEFAULT_INST
, 2);
347 af_fwrite("INST", 4, 1, file
->fh
);
348 af_write_uint32_be(&length
, file
->fh
);
350 instrumentdata
.baseNote
=
351 afGetInstParamLong(file
, AF_DEFAULT_INST
, AF_INST_MIDI_BASENOTE
);
352 af_write_uint8(&instrumentdata
.baseNote
, file
->fh
);
353 instrumentdata
.detune
=
354 afGetInstParamLong(file
, AF_DEFAULT_INST
, AF_INST_NUMCENTS_DETUNE
);
355 af_write_uint8(&instrumentdata
.detune
, file
->fh
);
356 instrumentdata
.lowNote
=
357 afGetInstParamLong(file
, AF_DEFAULT_INST
, AF_INST_MIDI_LONOTE
);
358 af_write_uint8(&instrumentdata
.lowNote
, file
->fh
);
359 instrumentdata
.highNote
=
360 afGetInstParamLong(file
, AF_DEFAULT_INST
, AF_INST_MIDI_HINOTE
);
361 af_write_uint8(&instrumentdata
.highNote
, file
->fh
);
362 instrumentdata
.lowVelocity
=
363 afGetInstParamLong(file
, AF_DEFAULT_INST
, AF_INST_MIDI_LOVELOCITY
);
364 af_write_uint8(&instrumentdata
.lowVelocity
, file
->fh
);
365 instrumentdata
.highVelocity
=
366 afGetInstParamLong(file
, AF_DEFAULT_INST
, AF_INST_MIDI_HIVELOCITY
);
367 af_write_uint8(&instrumentdata
.highVelocity
, file
->fh
);
369 instrumentdata
.gain
=
370 afGetInstParamLong(file
, AF_DEFAULT_INST
, AF_INST_NUMDBS_GAIN
);
371 af_write_uint16_be(&instrumentdata
.gain
, file
->fh
);
373 af_write_uint16_be(&instrumentdata
.sustainLoopPlayMode
, file
->fh
);
374 af_write_uint16_be(&instrumentdata
.sustainLoopBegin
, file
->fh
);
375 af_write_uint16_be(&instrumentdata
.sustainLoopEnd
, file
->fh
);
377 af_write_uint16_be(&instrumentdata
.releaseLoopPlayMode
, file
->fh
);
378 af_write_uint16_be(&instrumentdata
.releaseLoopBegin
, file
->fh
);
379 af_write_uint16_be(&instrumentdata
.releaseLoopEnd
, file
->fh
);
384 static status
WriteMARK (AFfilehandle file
)
386 AFfileoffset chunkStartPosition
, chunkEndPosition
;
394 numMarkers
= afGetMarkIDs(file
, AF_DEFAULT_TRACK
, NULL
);
398 aiff
= file
->formatSpecific
;
400 if (aiff
->MARK_offset
== 0)
401 aiff
->MARK_offset
= af_ftell(file
->fh
);
403 af_fseek(file
->fh
, aiff
->MARK_offset
, SEEK_SET
);
405 af_fwrite("MARK", 4, 1, file
->fh
);
406 af_write_uint32_be(&length
, file
->fh
);
408 chunkStartPosition
= af_ftell(file
->fh
);
410 markids
= _af_calloc(numMarkers
, sizeof (int));
412 afGetMarkIDs(file
, AF_DEFAULT_TRACK
, markids
);
414 af_write_uint16_be(&numMarkers
, file
->fh
);
416 for (i
=0; i
<numMarkers
; i
++)
418 uint8_t namelength
, zero
= 0;
424 position
= afGetMarkPosition(file
, AF_DEFAULT_TRACK
, markids
[i
]);
426 af_write_uint16_be(&id
, file
->fh
);
427 af_write_uint32_be(&position
, file
->fh
);
429 name
= afGetMarkName(file
, AF_DEFAULT_TRACK
, markids
[i
]);
432 /* Write the name as a Pascal-style string. */
433 af_write_pstring(name
, file
->fh
);
438 chunkEndPosition
= af_ftell(file
->fh
);
439 length
= chunkEndPosition
- chunkStartPosition
;
442 printf(" end: %d\n", chunkEndPosition
);
443 printf(" length: %d\n", length
);
446 af_fseek(file
->fh
, chunkStartPosition
- 4, SEEK_SET
);
448 af_write_uint32_be(&length
, file
->fh
);
449 af_fseek(file
->fh
, chunkEndPosition
, SEEK_SET
);
455 The FVER chunk, if present, is always the first chunk in the file.
457 static status
WriteFVER (AFfilehandle file
)
459 uint32_t chunkSize
, timeStamp
;
462 assert(file
->fileFormat
== AF_FILE_AIFFC
);
464 aiff
= file
->formatSpecific
;
466 if (aiff
->FVER_offset
== 0)
467 aiff
->FVER_offset
= af_ftell(file
->fh
);
469 af_fseek(file
->fh
, aiff
->FVER_offset
, SEEK_SET
);
471 af_fwrite("FVER", 4, 1, file
->fh
);
474 af_write_uint32_be(&chunkSize
, file
->fh
);
476 timeStamp
= AIFC_VERSION_1
;
477 af_write_uint32_be(&timeStamp
, file
->fh
);
483 WriteMiscellaneous writes all the miscellaneous data chunks in a
484 file handle structure to an AIFF or AIFF-C file.
486 static status
WriteMiscellaneous (AFfilehandle file
)
491 aiff
= (_AIFFInfo
*) file
->formatSpecific
;
493 if (aiff
->miscellaneousPosition
== 0)
494 aiff
->miscellaneousPosition
= af_ftell(file
->fh
);
496 af_fseek(file
->fh
, aiff
->miscellaneousPosition
, SEEK_SET
);
498 for (i
=0; i
<file
->miscellaneousCount
; i
++)
500 _Miscellaneous
*misc
= &file
->miscellaneous
[i
];
501 uint32_t chunkType
, chunkSize
;
505 printf("WriteMiscellaneous: %d, type %d\n", i
, misc
->type
);
511 memcpy(&chunkType
, "NAME", 4); break;
513 memcpy(&chunkType
, "AUTH", 4); break;
515 memcpy(&chunkType
, "(c) ", 4); break;
517 memcpy(&chunkType
, "ANNO", 4); break;
519 memcpy(&chunkType
, "MIDI", 4); break;
521 memcpy(&chunkType
, "APPL", 4); break;
525 af_fwrite(&chunkType
, 4, 1, file
->fh
);
527 chunkSize
= misc
->size
;
528 af_write_uint32_be(&chunkSize
, file
->fh
);
530 Write the miscellaneous buffer and then a pad byte
531 if necessary. If the buffer is null, skip the space
534 if (misc
->buffer
!= NULL
)
535 af_fwrite(misc
->buffer
, misc
->size
, 1, file
->fh
);
537 af_fseek(file
->fh
, misc
->size
, SEEK_CUR
);
539 if (misc
->size
% 2 != 0)
540 af_write_uint8(&padByte
, file
->fh
);